@leofcoin/peernet 0.10.8 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +49 -49
  3. package/coverage/lcov-report/block-navigation.js +8 -0
  4. package/coverage/lcov-report/codec-format-interface.js.html +224 -120
  5. package/coverage/lcov-report/dht-response.js.html +44 -39
  6. package/coverage/lcov-report/index.html +39 -64
  7. package/coverage/lcov-report/sorter.js +26 -0
  8. package/coverage/lcov.info +164 -424
  9. package/dist/browser/326.peernet.js +29 -0
  10. package/dist/browser/peernet.js +84258 -95769
  11. package/dist/commonjs/client-1a1f75e6.js +324 -0
  12. package/dist/commonjs/{codec-6367213c.js → codec-8c8c652f.js} +198 -188
  13. package/dist/commonjs/codec-format-interface.js +169 -152
  14. package/dist/commonjs/codec.js +4 -4
  15. package/dist/commonjs/dht-response.js +13 -13
  16. package/dist/commonjs/dht.js +24 -24
  17. package/dist/commonjs/hash.js +151 -141
  18. package/dist/commonjs/{http-a94c5a81.js → http-4bc6caeb.js} +19 -15
  19. package/dist/commonjs/peernet-message.js +15 -15
  20. package/dist/commonjs/peernet.js +1901 -1794
  21. package/dist/commonjs/request.js +13 -13
  22. package/dist/commonjs/response.js +13 -13
  23. package/dist/module/peernet.js +2462 -2348
  24. package/index.html +5 -7
  25. package/package.json +22 -14
  26. package/rollup.config.js +33 -5
  27. package/rollup0.config.js +7 -0
  28. package/src/client.js +75 -75
  29. package/src/codec/codec-format-interface.js +172 -155
  30. package/src/codec/codec.js +124 -114
  31. package/src/codec/codecs.js +79 -79
  32. package/src/dht/dht.js +121 -121
  33. package/src/discovery/peer-discovery.js +75 -75
  34. package/src/handlers/message.js +50 -52
  35. package/src/hash/hash.js +155 -145
  36. package/src/http/client/http-client.js +44 -44
  37. package/src/messages/chat-message.js +14 -14
  38. package/src/messages/data-response.js +14 -14
  39. package/src/messages/data.js +18 -18
  40. package/src/messages/dht-response.js +14 -15
  41. package/src/messages/dht.js +25 -25
  42. package/src/messages/peer-response.js +14 -14
  43. package/src/messages/peer.js +14 -14
  44. package/src/messages/peernet-message.js +14 -14
  45. package/src/messages/ps.js +14 -14
  46. package/src/messages/request.js +14 -14
  47. package/src/messages/response.js +14 -14
  48. package/src/peer.js +67 -67
  49. package/src/peernet.js +614 -697
  50. package/src/proto/chat-message.proto.js +7 -7
  51. package/src/proto/peernet.proto.js +2 -2
  52. package/src/utils/utils.js +78 -78
  53. package/test/codec.js +3 -2
  54. package/test/messages.js +7 -4
  55. package/test.js +11 -4
  56. package/webpack.config.js +41 -0
  57. package/coverage/lcov-report/codec.js.html +0 -677
  58. package/coverage/lcov-report/hash.js.html +0 -551
  59. package/debug.log +0 -3
  60. package/dist/browser/peernet.js.tmp-browserify-14074318104595318069 +0 -0
  61. package/dist/browser/peernet.js.tmp-browserify-45407634493269122267 +0 -0
  62. package/dist/browser/peernet.js.tmp-browserify-53722389064799025427 +0 -0
  63. package/dist/browser/peernet.js.tmp-browserify-96323030449218949300 +0 -0
  64. package/dist/codec/codec-format-interface.js +0 -433
  65. package/dist/codec/codec.js +0 -199
  66. package/dist/commonjs/codec-73adfc0f.js +0 -205
  67. package/dist/commonjs/http-2c603501.js +0 -324
  68. package/dist/commonjs/http-42a6e555.js +0 -324
  69. package/dist/commonjs/http-43f4fafe.js +0 -324
  70. package/dist/commonjs/peernet-message-b6925673.js +0 -32
  71. package/dist/hash/hash.js +0 -340
  72. package/dist/messages/dht-response.js +0 -454
  73. package/dist/messages/dht.js +0 -453
  74. package/dist/messages/peernet.js +0 -456
  75. package/dist/module/http-273664bd.js +0 -317
  76. package/dist/module/http-8fe3c0d7.js +0 -317
  77. package/dist/module/http-c780c991.js +0 -317
  78. package/dist/module/http-f13e0d77.js +0 -317
@@ -1,12 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var PubSub = require('@vandeurenglenn/little-pubsub');
4
- var sha256 = require('crypto-js/sha256');
5
- var P2P = require('p2pt');
6
- var LeofcoinStorage$1 = require('@leofcoin/storage');
7
- var websocket = require('websocket');
8
- var http$1 = require('http');
9
- var Koa = require('koa');
3
+ var LeofcoinStorage = require('@leofcoin/storage');
10
4
  var peernetMessage = require('./peernet-message.js');
11
5
  var dht = require('./dht.js');
12
6
  var dhtResponse = require('./dht-response.js');
@@ -14,786 +8,624 @@ var protons = require('protons');
14
8
  var codecFormatInterface = require('./codec-format-interface.js');
15
9
  var request = require('./request.js');
16
10
  var response = require('./response.js');
17
- var fetch$1 = require('node-fetch');
18
- var codec = require('./codec-6367213c.js');
11
+ var fetch = require('node-fetch');
12
+ var codec = require('./codec-8c8c652f.js');
19
13
  var hash = require('./hash.js');
20
- var MultiWallet = require('@leofcoin/multi-wallet');
21
- require('bs32');
22
- require('bs58');
23
- require('is-hex');
24
- require('varint');
25
- require('keccak');
14
+ var generateAccount = require('@leofcoin/generate-account');
15
+ var bs58check = require('bs58check');
16
+ var bip39 = require('bip39');
17
+ var bip32 = require('bip32');
18
+ var createKeccakHash = require('keccak');
19
+ var secp256k1 = require('secp256k1');
20
+ var ecc = require('tiny-secp256k1');
21
+ var MultiSignature = require('multi-signature');
22
+ var varint = require('varint');
23
+ var AES = require('crypto-js/aes.js');
24
+ require('crypto-js/sha512.js');
25
+ var ENC = require('crypto-js/enc-utf8.js');
26
+ require('@vandeurenglenn/base32');
27
+ require('@vandeurenglenn/base58');
28
+ require('@vandeurenglenn/is-hex');
26
29
 
27
30
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
28
31
 
29
- var PubSub__default = /*#__PURE__*/_interopDefaultLegacy(PubSub);
30
- var sha256__default = /*#__PURE__*/_interopDefaultLegacy(sha256);
31
- var P2P__default = /*#__PURE__*/_interopDefaultLegacy(P2P);
32
- var LeofcoinStorage__default = /*#__PURE__*/_interopDefaultLegacy(LeofcoinStorage$1);
33
- var Koa__default = /*#__PURE__*/_interopDefaultLegacy(Koa);
34
- var protons__default = /*#__PURE__*/_interopDefaultLegacy(protons);
35
- var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch$1);
36
- var MultiWallet__default = /*#__PURE__*/_interopDefaultLegacy(MultiWallet);
37
-
38
- class PeernetPeer {
39
- constructor(id, connection) {
40
- this._events = {};
41
- this.bw = {
42
- up: 0,
43
- down: 0,
44
- };
45
- this.id = id;
46
- this.connection = connection;
47
-
48
- this.connection.on('data', (message) => {
49
- this.bw.down += message.length;
50
- pubsub.publish('peernet.data', JSON.parse(message.toString()));
51
- });
52
- }
53
-
54
- request(data) {
55
- return new Promise((resolve, reject) => {
56
- const id = Math.random().toString(36).slice(-12);
57
- data = Buffer.from(JSON.stringify({id, data}));
58
- const _onData = (message) => {
59
- if (message.id !== id) return
60
-
61
- resolve(message.data);
62
- };
63
-
64
- pubsub.subscribe('peernet.data', _onData);
65
-
66
- // cleanup subscriptions
67
- setTimeout(() => {
68
- pubsub.unsubscribe('peernet.data', _onData);
69
- }, 5000);
70
-
71
- this.write(data);
32
+ function _interopNamespace(e) {
33
+ if (e && e.__esModule) return e;
34
+ var n = Object.create(null);
35
+ if (e) {
36
+ Object.keys(e).forEach(function (k) {
37
+ if (k !== 'default') {
38
+ var d = Object.getOwnPropertyDescriptor(e, k);
39
+ Object.defineProperty(n, k, d.get ? d : {
40
+ enumerable: true,
41
+ get: function () { return e[k]; }
42
+ });
43
+ }
72
44
  });
73
45
  }
46
+ n["default"] = e;
47
+ return Object.freeze(n);
48
+ }
74
49
 
75
- write(data) {
76
- if (!Buffer.isBuffer(data)) data = Buffer.from(data);
77
-
78
- this.bw.up += data.length;
79
- this.connection.write(data);
80
- }
81
-
82
- on(event = 'peernet.data', cb) {
83
- this._events[event] = cb;
84
- pubsub.subscribe(event, cb);
85
- // this.connection.on(event, cb)
86
- }
87
-
88
- removeListener(event = 'data', cb) {
89
- delete this._events[event];
90
- pubsub.unsubscribe(event, cb);
50
+ var LeofcoinStorage__default = /*#__PURE__*/_interopDefaultLegacy(LeofcoinStorage);
51
+ var protons__default = /*#__PURE__*/_interopDefaultLegacy(protons);
52
+ var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
53
+ var generateAccount__default = /*#__PURE__*/_interopDefaultLegacy(generateAccount);
54
+ var bs58check__default = /*#__PURE__*/_interopDefaultLegacy(bs58check);
55
+ var bs58check__namespace = /*#__PURE__*/_interopNamespace(bs58check);
56
+ var bip32__namespace = /*#__PURE__*/_interopNamespace(bip32);
57
+ var createKeccakHash__default = /*#__PURE__*/_interopDefaultLegacy(createKeccakHash);
58
+ var ecc__default = /*#__PURE__*/_interopDefaultLegacy(ecc);
59
+ var MultiSignature__default = /*#__PURE__*/_interopDefaultLegacy(MultiSignature);
60
+ var varint__default = /*#__PURE__*/_interopDefaultLegacy(varint);
61
+ var AES__default = /*#__PURE__*/_interopDefaultLegacy(AES);
62
+ var ENC__default = /*#__PURE__*/_interopDefaultLegacy(ENC);
63
+
64
+ /* socket-request-client version 1.6.1 */
65
+
66
+ class LittlePubSub {
67
+ constructor(verbose = true) {
68
+ this.subscribers = {};
69
+ this.verbose = verbose;
70
+ }
71
+ subscribe(event, handler, context) {
72
+ if (typeof context === 'undefined') {
73
+ context = handler;
74
+ }
75
+ this.subscribers[event] = this.subscribers[event] || { handlers: [], value: null};
76
+ this.subscribers[event].handlers.push(handler.bind(context));
91
77
  }
92
-
93
- close() {
94
- for (const event of Object.keys(this._events)) {
95
- pubsub.unsubscribe(event, this._events[event]);
78
+ unsubscribe(event, handler, context) {
79
+ if (typeof context === 'undefined') {
80
+ context = handler;
96
81
  }
97
- this._events = [];
98
-
99
- for (const event of this.connection._events.data) {
100
- this.connection.removeListener('data', event);
82
+ if (this.subscribers[event]) {
83
+ const index = this.subscribers[event].handlers.indexOf(handler.bind(context));
84
+ this.subscribers[event].handlers.splice(index);
85
+ if (this.subscribers[event].handlers.length === 0) delete this.subscribers[event];
101
86
  }
102
- this.connection.destroy();
103
87
  }
104
- }
105
-
106
- /**
107
- * Array of peers
108
- * @type {Array}
109
- * @property {PeernetPeer} peer Instance of Peer
110
- */
111
- globalThis.connections = new Map();
112
- globalThis.recentConnections = new Map();
113
- globalThis.pubsub = globalThis.pubsub || new PubSub__default["default"]({verbose: false});
114
-
115
- class PeernetClient {
116
- constructor(options = {}) {
117
- if (!options.id) options.id = Buffer.from('00000000000000000000000000000000');
118
- if (!options.networkVersion) options.networkVersion = 'v0.1.0';
119
- if (!options.networkName) options.networkName = 'peernet';
120
- this.id = options.id;
121
-
122
- this.topic = Buffer.from(sha256__default["default"](`${options.networkName}-${options.networkVersion}`).toString());
123
-
124
- const trackers = [
125
- 'wss://star.leofcoin.org',
126
- 'wss://tracker.openwebtorrent.com',
127
- // 'wss://tracker.sloppyta.co:443/announce',
128
- ];
129
- this.p2p = new P2P__default["default"](trackers, this.topic.slice(0, 20));
130
- this.p2p.on('peerconnect', (peer) => {
131
- peer = new PeernetPeer(peer.id, peer);
132
- connections.set(peer.id, peer);
133
- pubsub.publish('peer:discovered', peer);
134
- });
135
-
136
- this.p2p.on('peerclose', (peer) => {
137
- // TODO: close peernetPeer
138
- const peernetPeer = connections.get(peer.id);
139
- if (peernetPeer) {
140
- peernetPeer.close();
88
+ publish(event, change) {
89
+ if (this.subscribers[event]) {
90
+ if (this.verbose || this.subscribers[event].value !== change) {
91
+ this.subscribers[event].value = change;
92
+ this.subscribers[event].handlers.forEach(handler => {
93
+ handler(change, this.subscribers[event].value);
94
+ });
141
95
  }
142
- connections.delete(peer.id);
143
- pubsub.publish('peer:disconnected', peer);
144
- });
145
-
146
- this.p2p.start();
147
-
148
- if (globalThis.process) {
149
- process.on('SIGINT', async () => {
150
- console.log('Caught interrupt signal');
151
- this.close();
152
- setTimeout(async () => {
153
- process.exit();
154
- }, 100);
155
- });
156
- } else {
157
- globalThis.onbeforeunload = () => {
158
- this.close();
159
- };
160
96
  }
161
- //
162
- // this.sw.on('close', () => {
163
- // })
164
- }
165
-
166
- close() {
167
- return this.p2p.destroy()
168
- }
169
-
170
- _peers() {
171
- return this.p2p.getPeers()
172
97
  }
173
98
  }
174
99
 
175
- var version = "0.10.7";
176
-
177
- var api$1 = {
178
- version: ({send}) => send({client: '@peernet/api/http', version}),
179
- ready: ({send}) => {
180
- if (globalThis.states.ready) send(true);
181
- else pubsub.subscribe('ready', () => send(true));
182
- },
183
- storage: async (params, {send, error}) => {
184
- console.log(params);
185
- const {name, root, key, value, method} = params;
186
- try {
187
- if (name && root) {
188
- globalThis[name] = globalThis[name] || await new LeofcoinStorage(name, root);
189
- } else {
190
- return error('Expected name & root')
191
- }
192
- if (method === 'put') {
193
- await globalThis[name].put(key, value);
194
- return send('ok')
195
- }
196
- if (method === 'remove') {
197
- await globalThis[name].remove(key, value);
198
- return send('ok')
199
- }
200
- value = await globalThis[name].get(key);
201
- return send(value)
202
- } catch (e) {
203
- return error(e)
204
- }
205
- },
206
- getConfig: async (params, {send, error}) => {
207
- try {
208
- const config = await api.getConfig(params);
209
- send(config);
210
- } catch (e) {
211
- error(e);
212
- }
213
- },
214
- setMinerConfig: async (params, {send, error}) => {
215
- try {
216
- await api.setMinerConfig(params);
217
- send('ok');
218
- } catch (e) {
219
- error(e);
100
+ var clientApi = _pubsub => {
101
+ const subscribe = (topic, cb) => {
102
+ _pubsub.subscribe(topic, cb);
103
+ };
104
+ const unsubscribe = (topic, cb) => {
105
+ _pubsub.unsubscribe(topic, cb);
106
+ };
107
+ const publish = (topic, value) => {
108
+ _pubsub.publish(topic, value);
109
+ };
110
+ const _connectionState = (state) => {
111
+ switch (state) {
112
+ case 0:
113
+ return 'connecting'
114
+ case 1:
115
+ return 'open'
116
+ case 2:
117
+ return 'closing'
118
+ case 3:
119
+ return 'closed'
220
120
  }
221
- },
222
- getMinerConfig: async ({send, error}) => {
223
- try {
224
- const config = await api.getMinerConfig();
225
- send(config);
226
- } catch (e) {
227
- error(e);
121
+ };
122
+ const request = (client, request) => {
123
+ return new Promise((resolve, reject) => {
124
+ const state = _connectionState(client.readyState);
125
+ if (state !== 'open') return reject(`coudn't send request to ${client.id}, no open connection found.`)
126
+ request.id = Math.random().toString(36).slice(-12);
127
+ const handler = result => {
128
+ if (result && result.error) return reject(result.error)
129
+ resolve({result, id: request.id, handler});
130
+ unsubscribe(request.id, handler);
131
+ };
132
+ subscribe(request.id, handler);
133
+ send(client, request);
134
+ });
135
+ };
136
+ const send = async (client, request) => {
137
+ return client.send(JSON.stringify(request))
138
+ };
139
+ const pubsub = client => {
140
+ return {
141
+ publish: (topic = 'pubsub', value) => {
142
+ return send(client, {url: 'pubsub', params: { topic, value }})
143
+ },
144
+ subscribe: (topic = 'pubsub', cb) => {
145
+ subscribe(topic, cb);
146
+ return send(client, {url: 'pubsub', params: { topic, subscribe: true }})
147
+ },
148
+ unsubscribe: (topic = 'pubsub', cb) => {
149
+ unsubscribe(topic, cb);
150
+ return send(client, {url: 'pubsub', params: { topic, unsubscribe: true }})
151
+ },
152
+ subscribers: _pubsub.subscribers
228
153
  }
229
- },
230
- wallet: async ({send}) => {
231
- const wallet = await walletStore.get();
232
- send(wallet);
233
- },
234
- addresses: async ({send, error}) => {
235
- try {
236
- const adresses = await api.addresses();
237
- send(adresses);
238
- } catch (e) {
239
- error(e);
154
+ };
155
+ const server = (client) => {
156
+ return {
157
+ uptime: async () => {
158
+ try {
159
+ const { result, id, handler } = await request(client, {url: 'uptime'});
160
+ unsubscribe(id, handler);
161
+ return result
162
+ } catch (e) {
163
+ throw e
164
+ }
165
+ },
166
+ ping: async () => {
167
+ try {
168
+ const now = new Date().getTime();
169
+ const { result, id, handler } = await request(client, {url: 'ping'});
170
+ unsubscribe(id, handler);
171
+ return (Number(result) - now)
172
+ } catch (e) {
173
+ throw e
174
+ }
175
+ }
240
176
  }
241
- },
242
- accountNames: async (params, {send, error}) => {
243
- try {
244
- const adresses = await api.accountNames(params.index);
245
- send(adresses);
246
- } catch (e) {
247
- error(e);
177
+ };
178
+ const peernet = (client) => {
179
+ return {
180
+ join: async (params) => {
181
+ try {
182
+ params.join = true;
183
+ const requested = { url: 'peernet', params };
184
+ const { result, id, handler } = await request(client, requested);
185
+ unsubscribe(id, handler);
186
+ return result
187
+ } catch (e) {
188
+ throw e
189
+ }
190
+ },
191
+ leave: async (params) => {
192
+ try {
193
+ params.join = false;
194
+ const requested = { url: 'peernet', params };
195
+ const { result, id, handler } = await request(client, requested);
196
+ unsubscribe(id, handler);
197
+ return result
198
+ } catch (e) {
199
+ throw e
200
+ }
201
+ }
248
202
  }
249
- },
250
- accounts: async ({send}) => {
251
- const accounts = await accountStore.get();
252
- send(accounts);
253
- },
254
- account: async (params, {send}) => {
255
- const account = await accountStore.get(params);
256
- send(account);
257
- },
258
- balance: async (params, {send, error}) => {
259
- console.log('balance');
260
- try {
261
- console.log(await api.getBalanceForAddress(params.address));
262
- const value = await api.getBalanceForAddress(params.address);
263
- send(value);
264
- } catch (e) {
265
- console.log(e);
266
- error(e);
203
+ };
204
+ return { send, request, pubsub, server, subscribe, unsubscribe, publish, peernet }
205
+ };
206
+
207
+ if (!globalThis.PubSub) globalThis.PubSub = LittlePubSub;
208
+ if (!globalThis.pubsub) globalThis.pubsub = new LittlePubSub({verbose: false});
209
+ const socketRequestClient = (url, protocols = 'echo-protocol', options = { retry: false }) => {
210
+ const { retry } = options;
211
+ const api = clientApi(pubsub);
212
+ let tries = 0;
213
+ const onerror = error => {
214
+ if (pubsub.subscribers['error']) {
215
+ pubsub.publish('error', error);
216
+ } else {
217
+ console.error(error);
267
218
  }
268
- },
269
- balanceAfter: async (params, {send, error}) => {
270
- try {
271
- const value = await api.getBalanceForAddressAfter(params.address, params.index);
272
- send(value);
273
- } catch (e) {
274
- error(e);
219
+ };
220
+ const onmessage = message => {
221
+ const {value, url, status, id} = JSON.parse(message.data.toString());
222
+ const publisher = id ? id : url;
223
+ if (status === 200) {
224
+ pubsub.publish(publisher, value);
225
+ } else {
226
+ pubsub.publish(publisher, {error: value});
275
227
  }
276
- },
277
- mine: async (params, {send, error}) => {
278
- api.mine(params);
279
- send('ok');
280
- },
281
- lastBlock: async ({send, error}) => {
282
- try {
283
- const value = await api.lastBlock();
284
- send(value);
285
- } catch (e) {
286
- error(e);
228
+ };
229
+ const clientConnection = client => {
230
+ const startTime = new Date().getTime();
231
+ return {
232
+ client,
233
+ request: async req => {
234
+ const { result, id, handler } = await api.request(client, req);
235
+ pubsub.unsubscribe(id, handler);
236
+ return result
237
+ },
238
+ send: req => api.send(client, req),
239
+ subscribe: api.subscribe,
240
+ unsubscribe: api.unsubscribe,
241
+ subscribers: api.subscribers,
242
+ publish: api.publish,
243
+ pubsub: api.pubsub(client),
244
+ uptime: () => {
245
+ const now = new Date().getTime();
246
+ return (now - startTime)
247
+ },
248
+ peernet: api.peernet(client),
249
+ server: api.server(client),
250
+ close: exit => {
251
+ client.onclose = message => {
252
+ if (exit) process.exit();
253
+ };
254
+ client.close();
255
+ }
287
256
  }
288
- },
289
- };
290
-
291
- const fullLog = text => {
292
- return console.log(`${new Date()}: ${text}`)
257
+ };
258
+ return new Promise((resolve, reject) => {
259
+ const init = () => {
260
+ let ws;
261
+ if (typeof process === 'object' && !globalThis.WebSocket) {
262
+ ws = require('websocket').w3cwebsocket;
263
+ } else {
264
+ ws = WebSocket;
265
+ }
266
+ const client = new ws(url, protocols);
267
+ client.onmessage = onmessage;
268
+ client.onerror = onerror;
269
+ client.onopen = () => {
270
+ tries = 0;
271
+ resolve(clientConnection(client));
272
+ };
273
+ client.onclose = message => {
274
+ tries++;
275
+ if (!retry) return reject(options)
276
+ if (tries > 5) {
277
+ console.log(`${protocols} Client Closed`);
278
+ console.error(`could not connect to - ${url}/`);
279
+ return resolve(clientConnection(client))
280
+ }
281
+ if (message.code === 1006) {
282
+ console.log('Retrying in 10 seconds');
283
+ setTimeout(() => {
284
+ return init();
285
+ }, retry);
286
+ }
287
+ };
288
+ };
289
+ return init();
290
+ });
293
291
  };
294
292
 
295
- const originIsAllowed = (requestOrigin, origin) => {
296
- // put logic here to detect whether the specified origin is allowed.
297
- if (origin && requestOrigin !== origin) return false;
298
- return true;
299
- };
293
+ class Peer {
294
+ #connection
295
+ #ready = false
296
+ #connecting = false
297
+ #connected = false
298
+ #channelReady = false
299
+ #destroying = false
300
+ #destroyed = false
301
+ #isNegotiating = false
302
+ #firstNegotiation = true
303
+ #iceComplete = false
304
+ #remoteTracks = []
305
+ #remoteStreams = []
306
+ #pendingCandidates = []
307
+ #senderMap = new Map()
308
+ #iceCompleteTimer
309
+ #channel
310
+
311
+ get connection() {
312
+ return this.#connection
313
+ }
314
+
315
+ get connected() {
316
+ return this.#connected
317
+ }
300
318
 
301
319
  /**
302
- * @module socketResponse
303
- *
304
- * @param {object} connection socket connection
305
- * @param {string} route the route to handle
306
- */
307
- var socketConnection = (request, protocol, origin) => {
308
- if (origin && !originIsAllowed(request.origin, origin)) {
309
- // Make sure we only accept requests from an allowed origin
310
- request.reject();
311
- fullLog(`Connection from origin ${request.origin} rejected.`);
312
- return;
313
- }
314
- // console.log(request);
315
- const connection = request.accept(protocol, request.origin);
316
- fullLog(`Connection accepted @${protocol}`);
317
- return connection;
318
- };
319
-
320
- /**
321
- * @module socketResponse
322
- *
323
- * @param {object} connection socket connection
324
- * @param {string} url the request url
320
+ * @params {Object} options
321
+ * @params {string} options.channelName - this peerid : otherpeer id
325
322
  */
326
- var socketResponse = (connection, url, id, customEvent) => {
327
- const send = (data = 'ok', status = 200) => connection.send(
328
- JSON.stringify({url, status, value: data, id, customEvent})
329
- );
330
- const error = data => connection.send(JSON.stringify({url, value: data}));
331
- return {
332
- connection,
333
- send,
334
- error
335
- }
336
- };
337
-
338
- const socketRequestServer = (options, routes = {}) => {
339
- // if (!routes && !routes.port && options) routes = options;
340
- // else if (!options && !routes) return console.error('no routes defined');
341
-
342
- let {httpServer, httpsServer, port, protocol, credentials, origin, pubsub } = options;
343
- if (!pubsub) pubsub = new PubSub__default["default"]({verbose: false});
344
- if (!port) port = 6000;
345
- const connections = [];
346
- let connection;
347
- const startTime = new Date().getTime();
348
- // default routes
349
- if (!routes.ping) routes.ping = (response) => response.send(new Date().getTime());
350
- if (!routes.uptime) routes.uptime = (response) => {
351
- const now = new Date().getTime();
352
- response.send(now - startTime);
323
+ constructor(options = {}) {
324
+ this._in = this._in.bind(this);
325
+ this.offerOptions = options.offerOptions;
326
+ this.initiator = options.initiator;
327
+ this.streams = options.streams;
328
+ this.socketClient = options.socketClient;
329
+ this.id = options.id;
330
+ this.to = options.to;
331
+ this.bw = {
332
+ up: 0,
333
+ down: 0
353
334
  };
354
- if (!routes.pubsub) {
355
- routes.pubsub = (params, response) => {
356
- if (!response) {
357
- response = params;
358
- params = {};
359
- }
360
-
361
- if (!params.topic) params.topic = 'pubsub';
362
-
363
- const topic = params.topic;
364
- delete params.topic;
365
-
366
- if (params.subscribe) {
367
- pubsub.subscribe(topic, message => {
368
- response.connection.send(JSON.stringify({url: topic, status: 200, value: message}));
369
- });
370
- response.send('ok', 200);
371
- } else if (params.unsubscribe) {
372
- pubsub.unsubscribe(topic, message => {
373
- response.connection.send(JSON.stringify({url: topic, status: 200, value: message}));
374
- });
375
- for (const connection of connections) {
376
- if (connection !== response.connection) connection.send(JSON.stringify({url: topic, status: 200, value: params}));
377
- }
378
- response.send('ok', 200);
379
- }
380
- else if (params.value !== undefined)
381
- // should only be send raw to stars
382
- // for (const connection of connections) {
383
- // if (connection !== response.connection) connection.send(JSON.stringify({
384
- // url: topic, status: 200, value: params
385
- // }));
386
- // }
387
- pubsub.publish(topic, params.value);
388
- response.send('ok', 200);
389
- };
390
-
391
- }
392
- globalThis.peerMap = new Map();
393
- if (!routes.peernet) {
394
- routes.peernet = (params, response) => {
395
- if (params.join) {
396
- peerMap.set(params.peerId, params.address);
397
- response.send([...peerMap.values()]);
398
- for (const connection of connections) {
399
- if (connection !== response.connection)
400
- socketResponse(connection, 'peernet', 'peernet').send({discovered: params.address});
401
- }
402
- return
403
- }
404
- if (!params.join) {
405
- peerMap.delete(params.peerId);
406
- return response.send()
335
+
336
+ this.channelName = options.channelName || Buffer.from(Math.random().toString(36).slice(-12)).toString('hex');
337
+ console.log(this.channelName);
338
+ this.options = options;
339
+ this.#init();
340
+ }
341
+
342
+ set socketClient(value) {
343
+ // this.socketClient?.pubsub.unsubscribe('signal', this._in)
344
+ this._socketClient = value;
345
+ this._socketClient.pubsub.subscribe('signal', this._in);
346
+ }
347
+
348
+ get socketClient() {
349
+ return this._socketClient
350
+ }
351
+
352
+ send(message) {
353
+ this.bw.up += message.length;
354
+ this.channel.send(message);
355
+ }
356
+
357
+ request(data) {
358
+ return new Promise((resolve, reject) => {
359
+ const id = Math.random().toString(36).slice(-12);
360
+ data = new TextEncoder().encode(JSON.stringify({id, data}));
361
+ const _onData = message => {
362
+ message = JSON.parse(new TextDecoder().decode(message.data));
363
+ if (message.id === id) {
364
+ resolve(message.data);
365
+ pubsub.unsubscribe('peer:data', _onData);
407
366
  }
408
367
  };
409
- }
410
- // if (!protocol) protocol = 'echo-protocol';
411
- if (!httpServer && !httpsServer) {
412
- const { createServer } = credentials ? require('https') : require('http');
413
- if (credentials) httpServer = createServer(credentials);
414
- else httpServer = createServer();
415
-
416
- httpServer.listen(port, () => {
417
- console.log(`listening on ${port}`);
368
+
369
+ pubsub.subscribe('peer:data', _onData);
370
+
371
+ // cleanup subscriptions
372
+ setTimeout(() => {
373
+ pubsub.unsubscribe('peer:data', _onData);
374
+ }, 5000);
375
+
376
+ this.send(data);
418
377
  });
419
378
  }
420
379
 
421
- const socketServer = new websocket.server({
422
- httpServer,
423
- autoAcceptConnections: false
424
- });
380
+ async #init() {
381
+ try {
382
+ const iceServers = [{
383
+ urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
384
+ }];
425
385
 
426
- socketServer.on('request', request => {
386
+ this.#connection = new wrtc.RTCPeerConnection();
387
+ this.#connection.onicecandidate = ({ candidate }) => {
388
+ if (candidate) {
389
+ this.address = candidate.address;
390
+ this.port = candidate.port;
391
+ this.protocol = candidate.protocol;
392
+ this.ipFamily = this.address.includes('::') ? 'ipv6': 'ipv4';
393
+ this._sendMessage({candidate});
394
+ }
395
+ };
396
+ // if (this.initiator) this.#connection.onnegotiationneeded = () => {
397
+ // console.log('create offer');
398
+ this.#connection.ondatachannel = (message) => {
399
+ message.channel.onopen = () => {
400
+ this.#connected = true;
401
+ pubsub.publish('peer:connected', this);
402
+ };
403
+ message.channel.onclose = () => console.log('close');
404
+ message.channel.onmessage = (message) => {
405
+ if (message.to) {
406
+ if (message.to === this.id) pubsub.publish('peer:data', message);
407
+ } else pubsub.publish('peer:data', message);
408
+ this.bw.down += message.length;
409
+ };
410
+ this.channel = message.channel;
411
+ };
412
+ if (this.initiator) {
427
413
 
428
- connection = socketConnection(request, protocol, origin);
429
- connections.push(connection);
414
+ this.channel = this.#connection.createDataChannel('messageChannel');
415
+ this.channel.onopen = () => {
416
+ this.#connected = true;
417
+ pubsub.publish('peer:connected', this);
418
+ // this.channel.send('hi')
419
+ };
420
+ this.channel.onclose = () => this.close.bind(this);
430
421
 
431
- const routeHandler = message => {
432
- let data;
433
- if (message.type) {
434
- switch (message.type) {
435
- case 'binary':
436
- data = message.binaryData.toString();
437
- break;
438
- case 'utf8':
439
- data = message.utf8Data;
440
- break;
441
- }
442
- }
443
- const { route, params, url, id, customEvent } = JSON.parse(data);
444
- // ignore api when customEvent is defined
445
- if (customEvent) return;
446
- if (routes[url]) {
447
- if (!params) return routes[url](socketResponse(connection, url, id));
448
- return routes[url](params, socketResponse(connection, url, id));
422
+ this.channel.onmessage = (message) => {
423
+ if (message.to) {
424
+ if (message.to === this.id) pubsub.publish('peer:data', message);
425
+ } else pubsub.publish('peer:data', message);
426
+ this.bw.down += message.length;
427
+ };
428
+
429
+ const offer = await this.#connection.createOffer();
430
+ await this.#connection.setLocalDescription(offer);
431
+
432
+ this._sendMessage({'sdp': this.#connection.localDescription});
449
433
  }
450
- else return socketResponse(connection, url, id).error(`nop handler found for '${message.url}'`);
451
- };
434
+ // }
452
435
 
453
- connection.on('message', routeHandler);
454
- });
436
+ } catch (e) {
437
+ console.log(e);
438
+ }
439
+ }
455
440
 
456
- return {
457
- close: () => socketServer.shutDown(),
458
- connections
459
- };
460
- };
461
-
462
- var http = (config = {}) => {
463
- if (typeof config !== 'object') config = {};
464
- if (!config.protocol) config.protocol = 'peernet-v0.1.0';
465
- if (!config.port) config.port = 2000;
466
- if (!config.host) config.host = '127.0.0.1';
467
-
468
- const app = new Koa__default["default"]();
469
-
470
- app.use(async (ctx) => {
471
- const url = ctx.url.split('/api/')[1];
472
- if (url === 'version') ctx.body = {client: '@peernet/api/http', version};
473
- });
474
-
475
- const httpServer = http$1.createServer(app.callback());
476
-
477
- config.httpServer = httpServer;
478
-
479
- httpServer.listen(config.port, () => {
480
- console.log(`listening on ${config.port}`);
481
- });
482
-
483
- return socketRequestServer(config, api$1)
484
- };
485
-
486
- var clientApi = _pubsub => {
487
-
488
- const subscribe = (topic, cb) => {
489
- _pubsub.subscribe(topic, cb);
490
- };
491
-
492
- const unsubscribe = (topic, cb) => {
493
- _pubsub.unsubscribe(topic, cb);
494
- };
495
-
496
- const publish = (topic, value) => {
497
- _pubsub.publish(topic, value);
498
- };
499
-
500
- const _connectionState = (state) => {
501
- switch (state) {
502
- case 0:
503
- return 'connecting'
504
- case 1:
505
- return 'open'
506
- case 2:
507
- return 'closing'
508
- case 3:
509
- return 'closed'
510
- }
511
- };
512
- /**
513
- * @param {string} type
514
- * @param {string} name
515
- * @param {object} params
516
- */
517
- const request = (client, request) => {
518
- return new Promise((resolve, reject) => {
519
-
520
- const state = _connectionState(client.readyState);
521
- if (state !== 'open') return reject(`coudn't send request to ${client.id}, no open connection found.`)
522
-
523
- request.id = Math.random().toString(36).slice(-12);
524
- const handler = result => {
525
- if (result && result.error) return reject(result.error)
526
- resolve({result, id: request.id, handler});
527
- };
528
- subscribe(request.id, handler);
529
- send(client, request);
530
- });
531
- };
532
-
533
- const send = async (client, request) => {
534
- return client.send(JSON.stringify(request))
535
- };
441
+ _sendMessage(message) {
442
+ this.socketClient.send({url: 'signal', params: {
443
+ to: this.to,
444
+ from: this.id,
445
+ channelName: this.options.channelName,
446
+ ...message
447
+ }});
448
+ }
536
449
 
537
- const pubsub = client => {
538
- return {
539
- publish: (topic = 'pubsub', value) => {
540
- return send(client, {url: 'pubsub', params: { topic, value }})
541
- },
542
- subscribe: (topic = 'pubsub', cb) => {
543
- subscribe(topic, cb);
544
- return send(client, {url: 'pubsub', params: { topic, subscribe: true }})
545
- },
546
- unsubscribe: (topic = 'pubsub', cb) => {
547
- unsubscribe(topic, cb);
548
- return send(client, {url: 'pubsub', params: { topic, unsubscribe: true }})
549
- },
550
- subscribers: _pubsub.subscribers
450
+ async _in(message, data) {
451
+ // message = JSON.parse(message);
452
+ if (message.to !== this.id) return
453
+ // if (data.videocall) return this._startStream(true, false); // start video and audio stream
454
+ // if (data.call) return this._startStream(true, true); // start audio stream
455
+ if (message.candidate) {
456
+ this.remoteAddress = message.candidate.address;
457
+ this.remotePort = message.candidate.port;
458
+ this.remoteProtocol = message.candidate.protocol;
459
+ this.remoteIpFamily = this.remoteAddress?.includes('::') ? 'ipv6': 'ipv4';
460
+ return this.#connection.addIceCandidate(new wrtc.RTCIceCandidate(message.candidate));
551
461
  }
552
- };
553
-
554
- const server = (client) => {
555
- return {
556
- uptime: async () => {
557
- try {
558
- const { result, id, handler } = await request(client, {url: 'uptime'});
559
- unsubscribe(id, handler);
560
- return result
561
- } catch (e) {
562
- throw e
462
+ try {
463
+ if (message.sdp) {
464
+ if (message.sdp.type === 'offer') {
465
+ await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
466
+ const answer = await this.#connection.createAnswer();
467
+ await this.#connection.setLocalDescription(answer);
468
+ this._sendMessage({'sdp': this.#connection.localDescription});
563
469
  }
564
- },
565
- ping: async () => {
566
- try {
567
- const now = new Date().getTime();
568
- const { result, id, handler } = await request(client, {url: 'ping'});
569
- unsubscribe(id, handler);
570
- return (Number(result) - now)
571
- } catch (e) {
572
- throw e
470
+ if (message.sdp.type === 'answer') {
471
+ await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
573
472
  }
473
+ }
474
+ } catch (e) {
475
+ console.log(e);
476
+ }
477
+ }
478
+
479
+ close() {
480
+ this.channel?.close();
481
+ this.#connection?.close();
482
+
483
+ this.socketClient.pubsub.unsubscribe('signal', this._in);
484
+ }
485
+ }
486
+
487
+ class Client {
488
+ #peerConnection
489
+ #connections = {}
490
+ #stars = {}
491
+
492
+ get connections() {
493
+ return { ...this.#connections }
494
+ }
495
+
496
+ constructor(id, identifiers = ['peernet-v0.1.0'], stars = []) {
497
+ this.id = id || Math.random().toString(36).slice(-12);
498
+ if (!Array.isArray(identifiers)) identifiers = [identifiers];
499
+ this.peerJoined = this.peerJoined.bind(this);
500
+ this.peerLeft = this.peerLeft.bind(this);
501
+ this.starLeft = this.starLeft.bind(this);
502
+ this.starJoined = this.starJoined.bind(this);
503
+
504
+ this._init(identifiers, stars);
505
+ }
506
+
507
+ async _init(identifiers, stars = []) {
508
+ if (stars.length === 0) {
509
+ stars.push('wss://star.leofcoin.org');
510
+ stars.push('ws://localhost:44444');
511
+ }
512
+ this.identifiers = identifiers;
513
+ this.starsConfig = stars;
514
+ // reconnectJob()
515
+
516
+ globalThis.wrtc = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('wrtc')); });
517
+ for (const star of stars) {
518
+ try {
519
+ this.socketClient = await socketRequestClient(star, identifiers[0]);
520
+ const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
521
+ this.socketClient.peerId = id;
522
+ this.#stars[id] = this.socketClient;
523
+ } catch (e) {
524
+ if (stars.indexOf(star) === stars.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
574
525
  }
575
526
  }
576
- };
577
-
578
- const peernet = (client) => {
579
- return {
580
- join: async (params) => {
581
- try {
582
- params.join = true;
583
- const requested = { url: 'peernet', params };
584
- const { result, id, handler } = await request(client, requested);
585
- unsubscribe(id, handler);
586
- return result
587
- } catch (e) {
588
- throw e
589
- }
590
- },
591
- leave: async (params) => {
527
+ const peers = await this.socketClient.peernet.join({id: this.id});
528
+ for (const id of peers) {
529
+ if (id !== this.id && !this.#connections[id]) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id});
530
+ }
531
+ this.setupListeners();
532
+
533
+ pubsub.subscribe('peer:connected', (peer) => {
534
+ // peer.send(JSON.stringify({data: 'hello', from: this.id, to: peer.to}))
535
+ console.log({peer: peer.to});
536
+ console.log({id: peer.id});
537
+ console.log({id: this.id});
538
+ });
539
+ // pubsub.subscribe('peer:data', (data) => console.log({data}))
540
+ }
541
+
542
+ setupListeners() {
543
+ this.socketClient.subscribe('peer:joined', this.peerJoined);
544
+ this.socketClient.subscribe('peer:left', this.peerLeft);
545
+ this.socketClient.subscribe('star:left', this.starLeft);
546
+ }
547
+
548
+ starJoined(id) {
549
+ if (this.#stars[id]) {
550
+ this.#stars[id].close();
551
+ delete this.#stars[id];
552
+ }
553
+ console.log(`star ${id} joined`);
554
+ }
555
+
556
+ async starLeft(id) {
557
+ if (this.#stars[id]) {
558
+ this.#stars[id].close();
559
+ delete this.#stars[id];
560
+ }
561
+ if (this.socketClient?.peerId === id) {
562
+
563
+ this.socketClient.unsubscribe('peer:joined', this.peerJoined);
564
+ this.socketClient.unsubscribe('peer:left', this.peerLeft);
565
+ this.socketClient.unsubscribe('star:left', this.starLeft);
566
+ this.socketClient.close();
567
+ this.socketClient = undefined;
568
+
569
+ for (const star of this.starsConfig) {
592
570
  try {
593
- params.join = false;
594
- const requested = { url: 'peernet', params };
595
- const { result, id, handler } = await request(client, requested);
596
- unsubscribe(id, handler);
597
- return result
571
+ this.socketClient = await socketRequestClient(star, this.identifiers[0]);
572
+ if (!this.socketClient?.client?._connection.connected) return
573
+ const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
574
+ this.#stars[id] = this.socketClient;
575
+
576
+ this.socketClient.peerId = id;
577
+
578
+ const peers = await this.socketClient.peernet.join({id: this.id});
579
+ this.setupListeners();
580
+ for (const id of peers) {
581
+ if (id !== this.id) {
582
+ // close connection
583
+ if (this.#connections[id]) {
584
+ if (this.#connections[id].connected) await this.#connections[id].close();
585
+ delete this.#connections[id];
586
+ }
587
+ // reconnect
588
+ if (id !== this.id) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id});
589
+ }
590
+
591
+ }
598
592
  } catch (e) {
599
- throw e
593
+ console.log(e);
594
+ if (this.starsConfig.indexOf(star) === this.starsConfig.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
600
595
  }
601
596
  }
602
597
  }
603
- };
604
-
605
- return { send, request, pubsub, server, subscribe, unsubscribe, publish, peernet }
606
- };
607
-
608
- const socketRequestClient = (url, protocols = 'echo-protocol', options = { retry: false, pubsub: false }) => {
609
- let { pubsub, retry } = options;
610
- if (!pubsub) pubsub = new PubSub__default["default"]({verbose: false});
611
-
612
- const api = clientApi(pubsub);
613
-
614
- let tries = 0;
615
-
616
- const onerror = error => {
617
- if (pubsub.subscribers['error']) {
618
- pubsub.publish('error', error);
619
- } else {
620
- console.error(error);
621
- }
622
- };
623
598
 
624
- const onmessage = message => {
625
- const {value, url, status, id} = JSON.parse(message.data.toString());
626
- const publisher = id ? id : url;
627
- if (status === 200) {
628
- pubsub.publish(publisher, value);
629
- } else {
630
- pubsub.publish(publisher, {error: value});
631
- }
599
+ console.log(`star ${id} left`);
632
600
 
633
- };
634
601
 
635
- const clientConnection = client => {
636
- const startTime = new Date().getTime();
637
- return {
638
- client,
639
- request: async req => {
640
- const { result, id, handler } = await api.request(client, req);
641
- pubsub.unsubscribe(id, handler);
642
- return result
643
- },
644
- send: req => api.send(client, req),
645
- subscribe: api.subscribe,
646
- unsubscribe: api.unsubscribe,
647
- subscribers: api.subscribers,
648
- publish: api.publish,
649
- pubsub: api.pubsub(client),
650
- uptime: () => {
651
- const now = new Date().getTime();
652
- return (now - startTime)
653
- },
654
- peernet: api.peernet(client),
655
- server: api.server(client),
656
- close: exit => {
657
- client.onclose = message => {
658
- if (exit) process.exit();
659
- };
660
- client.close();
661
- }
602
+ }
603
+
604
+ peerLeft(id) {
605
+ if (this.#connections[id]) {
606
+ this.#connections[id].close();
607
+ delete this.#connections[id];
662
608
  }
663
- };
664
-
665
- return new Promise((resolve, reject) => {
666
- const init = () => {
667
- let ws;
668
- if (typeof process === 'object') {
669
- ws = require('websocket').w3cwebsocket;
670
- } else {
671
- ws = WebSocket;
672
- }
673
- const client = new ws(url, protocols);
609
+ console.log(`peer ${id} left`);
610
+ }
674
611
 
675
- client.onmessage = onmessage;
676
- client.onerror = onerror;
677
- client.onopen = () => {
678
- tries = 0;
679
- resolve(clientConnection(client));
680
- };
681
- client.onclose = message => {
682
- tries++;
683
- if (!retry) return reject(options)
684
- if (tries > 5) {
685
- console.log(`${protocols} Client Closed`);
686
- console.error(`could not connect to - ${url}/`);
687
- return resolve(clientConnection(client))
688
- }
689
- if (message.code === 1006) {
690
- console.log('Retrying in 10 seconds');
691
- setTimeout(() => {
692
- return init();
693
- }, retry);
694
- }
695
- };
696
- };
697
- return init();
698
- });
699
- };
700
-
701
- class HttpClientApi$1 {
702
- constructor(config = {}) {
703
- if (!config.apiPath) config.apiPath = 'api';
704
-
705
- const address = `ws://${config.host}:${config.port}`;
706
-
707
- this.apiUrl = (url) => `${address}/${url}`;
708
- return (async () => {
709
- this.client = await socketRequestClient(address, config.protocol, {pubsub: config.pubsub, retry: 3000});
710
- return this
711
- })()
712
- }
612
+ peerJoined(id, signal) {
613
+ if (this.#connections[id]) {
614
+ if (this.#connections[id].connected) this.#connections[id].close();
615
+ delete this.#connections[id];
616
+ }
617
+ // RTCPeerConnection
618
+ this.#connections[id] = new Peer({initiator: true, channelName: `${this.id}:${id}`, socketClient: this.socketClient, id: this.id, to: id});
619
+ console.log(`peer ${id} joined`);
620
+ }
621
+
622
+
623
+ }
713
624
 
714
- async get(url, obj) {
715
- const headers = {};
716
- let body = null;
717
- let method = 'GET';
718
- if (obj) {
719
- method = 'POST';
720
- headers['Content-Type'] = 'application/json';
721
- body = JSON.stringify(obj);
722
- }
723
- let response = await this.client.request(url, {headers, body, method});
724
- const type = response.headers.get('content-type').split(';')[0];
725
- if (type==='application/json') response = await response.json();
726
- return response
727
- }
728
-
729
- async put(url, obj) {
730
- const headers = {};
731
- let body = {};
732
- if (obj) {
733
- headers['Content-Type'] = 'application/json';
734
- body = JSON.stringify(obj);
735
- }
736
-
737
- let response = await fetch(this.apiUrl(url), {method: 'PUT', headers, body});
738
- const type = response.headers.get('content-type').split(';')[0];
739
- if (type==='application/json') response = await response.json();
740
- return response
741
- }
742
- }
743
-
744
- class HttpClientApi extends HttpClientApi$1 {
745
- constructor(config = {}) {
746
- config.apiPath = 'api';
747
- return (async () => {
748
- await super(config);
749
-
750
- this.properties = {
751
- wallet: 'get',
752
- version: 'get',
753
- addresses: 'get',
754
- config: 'get',
755
- account: 'get',
756
- accounts: 'get',
757
- transaction: 'any',
758
- transactions: 'get',
759
- block: 'get',
760
- blocks: 'get',
761
- };
762
- this.keys = Object.keys(this.properties);
763
- return this
764
- })()
765
- }
766
-
767
- async request(url, data) {
768
- return await this.client.request({url, params: data})
769
- }
770
-
771
- async ready() {
772
- return await this.request('ready')
773
- }
774
-
775
- async version() {
776
- return await this.request('version')
777
- }
778
-
779
- async account(index) {
780
- return await this.request('account', {index})
781
- }
782
- }
783
-
784
- var httpClient = (config = {}) => {
785
- if (typeof config !== 'object') config = {};
786
- if (!config.protocol) config.protocol = 'peernet-v0.1.0';
787
- if (!config.port) config.port = 1000;
788
- if (!config.host) config.host = '127.0.0.1';
789
-
790
- return new HttpClientApi(config)
791
- };
792
-
793
- class LeofcoinStorageClient {
794
- constructor(name, root) {
795
- this.name = name;
796
- this.root = root;
625
+ class LeofcoinStorageClient {
626
+ constructor(name, root) {
627
+ this.name = name;
628
+ this.root = root;
797
629
  }
798
630
 
799
631
  async get(key) {
@@ -835,19 +667,19 @@ message PeernetDataMessage {
835
667
  }
836
668
  `;
837
669
 
838
- /**
839
- * @extends {CodecFormat}
840
- */
841
- class DataMessage extends codecFormatInterface {
842
- get keys() {
843
- return ['hash', 'store']
844
- }
845
- /**
846
- * @param {Buffer|String|Object|DataMessage} data - The data needed to create the DataMessage
847
- */
848
- constructor(data) {
849
- super(data, protons__default["default"](proto$5).PeernetDataMessage, {name: 'peernet-data'});
850
- }
670
+ /**
671
+ * @extends {CodecFormat}
672
+ */
673
+ class DataMessage extends codecFormatInterface {
674
+ get keys() {
675
+ return ['hash', 'store']
676
+ }
677
+ /**
678
+ * @param {Buffer|String|Object|DataMessage} data - The data needed to create the DataMessage
679
+ */
680
+ constructor(data) {
681
+ super(data, protons__default["default"](proto$5).PeernetDataMessage, {name: 'peernet-data'});
682
+ }
851
683
  }
852
684
 
853
685
  var proto$4 = `
@@ -857,15 +689,15 @@ message PsMessage {
857
689
  required bytes topic = 2;
858
690
  }`;
859
691
 
860
- class PsMessage extends codecFormatInterface {
861
- get keys() {
862
- return ['data', 'topic']
863
- }
864
-
865
- constructor(buffer) {
866
- const name = 'peernet-ps';
867
- super(buffer, protons__default["default"](proto$4).PsMessage, {name});
868
- }
692
+ class PsMessage extends codecFormatInterface {
693
+ get keys() {
694
+ return ['data', 'topic']
695
+ }
696
+
697
+ constructor(buffer) {
698
+ const name = 'peernet-ps';
699
+ super(buffer, protons__default["default"](proto$4).PsMessage, {name});
700
+ }
869
701
  }
870
702
 
871
703
  var proto$3 = `
@@ -875,15 +707,15 @@ message PeernetPeerMessage {
875
707
  }
876
708
  `;
877
709
 
878
- class PeerMessage extends codecFormatInterface {
879
- get keys() {
880
- return ['id']
881
- }
882
-
883
- constructor(data) {
884
- const name = 'peernet-peer';
885
- super(data, protons__default["default"](proto$3).PeernetPeerMessage, {name});
886
- }
710
+ class PeerMessage extends codecFormatInterface {
711
+ get keys() {
712
+ return ['id']
713
+ }
714
+
715
+ constructor(data) {
716
+ const name = 'peernet-peer';
717
+ super(data, protons__default["default"](proto$3).PeernetPeerMessage, {name});
718
+ }
887
719
  }
888
720
 
889
721
  var proto$2 = `
@@ -893,15 +725,15 @@ message PeernetPeerMessageResponse {
893
725
  }
894
726
  `;
895
727
 
896
- class PeerMessageResponse extends codecFormatInterface {
897
- get keys() {
898
- return ['id']
899
- }
900
-
901
- constructor(data) {
902
- const name = 'peernet-peer-response';
903
- super(data, protons__default["default"](proto$2).PeernetPeerMessageResponse, {name});
904
- }
728
+ class PeerMessageResponse extends codecFormatInterface {
729
+ get keys() {
730
+ return ['id']
731
+ }
732
+
733
+ constructor(data) {
734
+ const name = 'peernet-peer-response';
735
+ super(data, protons__default["default"](proto$2).PeernetPeerMessageResponse, {name});
736
+ }
905
737
  }
906
738
 
907
739
  var proto$1 = `
@@ -912,1058 +744,1333 @@ message PeernetDataMessageResponse {
912
744
  }
913
745
  `;
914
746
 
915
- class DataMessageResponse extends codecFormatInterface {
916
- get keys() {
917
- return ['hash', 'data']
918
- }
919
-
920
- constructor(data) {
921
- const name = 'peernet-data-response';
922
- super(data, protons__default["default"](proto$1).PeernetDataMessageResponse, {name});
923
- }
747
+ class DataMessageResponse extends codecFormatInterface {
748
+ get keys() {
749
+ return ['hash', 'data']
750
+ }
751
+
752
+ constructor(data) {
753
+ const name = 'peernet-data-response';
754
+ super(data, protons__default["default"](proto$1).PeernetDataMessageResponse, {name});
755
+ }
924
756
  }
925
757
 
926
- var proto = `
927
- message ChatMessage {
928
- required string value = 1;
929
- required string author = 2;
930
- required uint64 timestamp = 3;
931
- repeated string files = 4;
758
+ var proto = `
759
+ message ChatMessage {
760
+ required string value = 1;
761
+ required string author = 2;
762
+ required uint64 timestamp = 3;
763
+ repeated string files = 4;
932
764
  }`;
933
765
 
934
- class ChatMessage extends codecFormatInterface {
935
- get keys() {
936
- return ['author', 'value', 'timestamp', 'files']
937
- }
938
-
939
- constructor(buffer) {
940
- const name = 'chat-message';
941
- super(buffer, protons__default["default"](proto).ChatMessage, {name});
942
- }
943
- }
944
-
945
- const debug = (log) => {
946
- if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
947
- };
948
-
949
- const protoFor = (data) => {
950
- if (!Buffer.isBuffer(data)) data = Buffer.from(data);
951
- const codec$1 = new codec.PeernetCodec(data);
952
- if (!codec$1.name) throw new Error('proto not found')
953
- const Proto = globalThis.peernet.protos[codec$1.name];
954
- if (!Proto) throw (new Error(`No proto defined for ${codec$1.name}`))
955
- return new Proto(data)
956
- };
957
-
958
- /**
959
- * wether or not a peernet daemon is active
960
- * @return {Boolean}
961
- */
962
- const hasDaemon = async () => {
963
- try {
964
- let response = await fetch__default["default"]('http://127.0.0.1:1000/api/version');
965
- response = await response.json();
966
- return Boolean(response.client === '@peernet/api/http')
967
- } catch (e) {
968
- return false
969
- }
970
- };
971
-
972
- const https = () => {
973
- if (!globalThis.location) return false;
974
- return Boolean(globalThis.location.protocol === 'https:')
975
- };
976
-
977
- /**
978
- * Get current environment
979
- * @return {String} current environment [node, electron, browser]
980
- */
981
- const environment = () => {
982
- const _navigator = globalThis.navigator;
983
- if (!_navigator) {
984
- return 'node'
985
- } else if (_navigator && /electron/i.test(_navigator.userAgent)) {
986
- return 'electron'
987
- } else {
988
- return 'browser'
989
- }
990
- };
991
-
992
- /**
993
- * * Get current environment
994
- * @return {Object} result
995
- * @property {Boolean} reult.daemon whether or not daemon is running
996
- * @property {Boolean} reult.environment Current environment
997
- */
998
- const target = async () => {
999
- let daemon = false;
1000
- const env = await environment();
1001
- if (!https()) daemon = await hasDaemon();
1002
-
1003
- return {daemon, environment: env}
1004
- };
1005
-
1006
- class PeerDiscovery {
1007
- constructor(id) {
1008
- this.id = id;
1009
- }
1010
-
1011
- _getPeerId(id) {
1012
- if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
1013
-
1014
- for (const entry of [...peernet.peerMap.entries()]) {
1015
- for (const _id of entry[1]) {
1016
- if (_id === id) return entry[0]
1017
- }
1018
- }
1019
- }
1020
-
1021
- async discover(peer) {
1022
- let id = this._getPeerId(peer.id);
1023
- if (id) return id
1024
- const data = new peernet.protos['peernet-peer']({id: this.id});
1025
- const node = await peernet.prepareMessage(peer.id, data.encoded);
1026
-
1027
- let response = await peer.request(node.encoded);
1028
- response = protoFor(response);
1029
- response = new peernet.protos['peernet-peer-response'](response.decoded.data);
1030
-
1031
- id = response.decoded.id;
1032
- if (id === this.id) return;
1033
-
1034
- if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
1035
- else {
1036
- const connections = peernet.peerMap.get(id);
1037
- if (connections.indexOf(peer.id) === -1) {
1038
- connections.push(peer.id);
1039
- peernet.peerMap.set(peer.id, connections);
1040
- }
1041
- }
1042
- return id
1043
- }
1044
-
1045
- async discoverHandler(message, peer) {
1046
- const {id, proto} = message;
1047
- // if (typeof message.data === 'string') message.data = Buffer.from(message.data)
1048
- if (proto.name === 'peernet-peer') {
1049
- const from = proto.decoded.id;
1050
- if (from === this.id) return;
1051
-
1052
- if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
1053
- else {
1054
- const connections = peernet.peerMap.get(from);
1055
- if (connections.indexOf(peer.id) === -1) {
1056
- connections.push(peer.id);
1057
- peernet.peerMap.set(from, connections);
1058
- }
1059
- }
1060
- const data = new peernet.protos['peernet-peer-response']({id: this.id});
1061
- const node = await peernet.prepareMessage(from, data.encoded);
1062
-
1063
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1064
- } else if (proto.name === 'peernet-peer-response') {
1065
- const from = proto.decoded.id;
1066
- if (from === this.id) return;
1067
-
1068
- if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
1069
- else {
1070
- const connections = peernet.peerMap.get(from);
1071
- if (connections.indexOf(peer.id) === -1) {
1072
- connections.push(peer.id);
1073
- peernet.peerMap.set(from, connections);
1074
- }
1075
- }
1076
- }
1077
- }
766
+ class ChatMessage extends codecFormatInterface {
767
+ get keys() {
768
+ return ['author', 'value', 'timestamp', 'files']
769
+ }
770
+
771
+ constructor(buffer) {
772
+ const name = 'chat-message';
773
+ super(buffer, protons__default["default"](proto).ChatMessage, {name});
774
+ }
1078
775
  }
1079
776
 
1080
- /**
1081
- * Keep history of fetched address and ptr
1082
- * @property {Object} address
1083
- * @property {Object} ptr
1084
- */
1085
- const lastFetched = {
1086
- address: {
1087
- value: undefined,
1088
- timestamp: 0,
1089
- },
1090
- ptr: {
1091
- value: undefined,
1092
- timestamp: 0,
1093
- },
1094
- };
1095
-
1096
- const getAddress = async () => {
1097
- const {address} = lastFetched;
1098
- const now = Math.round(new Date().getTime() / 1000);
1099
- if (now - address.timestamp > 1200000) {
1100
- address.value = await fetch__default["default"]('https://icanhazip.com/');
1101
- address.value = await address.value.text();
1102
- address.timestamp = Math.round(new Date().getTime() / 1000);
1103
- lastFetched.address = address;
1104
- }
1105
-
1106
- return address.value
1107
- };
1108
-
1109
- const degreesToRadians = (degrees) => {
1110
- return degrees * Math.PI / 180;
1111
- };
1112
-
1113
- const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
1114
- const earthRadiusKm = 6371;
1115
-
1116
- const dLat = degreesToRadians(lat2-lat1);
1117
- const dLon = degreesToRadians(lon2-lon1);
1118
-
1119
- lat1 = degreesToRadians(lat1);
1120
- lat2 = degreesToRadians(lat2);
1121
- const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
1122
- Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
1123
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
1124
- return earthRadiusKm * c;
777
+ const debug = (log) => {
778
+ if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
779
+ };
780
+
781
+ const protoFor = (data) => {
782
+ if (!Buffer.isBuffer(data)) data = Buffer.from(data);
783
+ const codec$1 = new codec.PeernetCodec(data);
784
+ if (!codec$1.name) throw new Error('proto not found')
785
+ const Proto = globalThis.peernet.protos[codec$1.name];
786
+ if (!Proto) throw (new Error(`No proto defined for ${codec$1.name}`))
787
+ return new Proto(data)
788
+ };
789
+
790
+ /**
791
+ * wether or not a peernet daemon is active
792
+ * @return {Boolean}
793
+ */
794
+ const hasDaemon = async () => {
795
+ try {
796
+ let response = await fetch__default["default"]('http://127.0.0.1:1000/api/version');
797
+ response = await response.json();
798
+ return Boolean(response.client === '@peernet/api/http')
799
+ } catch (e) {
800
+ return false
801
+ }
802
+ };
803
+
804
+ const https = () => {
805
+ if (!globalThis.location) return false;
806
+ return Boolean(globalThis.location.protocol === 'https:')
807
+ };
808
+
809
+ /**
810
+ * Get current environment
811
+ * @return {String} current environment [node, electron, browser]
812
+ */
813
+ const environment = () => {
814
+ const _navigator = globalThis.navigator;
815
+ if (!_navigator) {
816
+ return 'node'
817
+ } else if (_navigator && /electron/i.test(_navigator.userAgent)) {
818
+ return 'electron'
819
+ } else {
820
+ return 'browser'
821
+ }
822
+ };
823
+
824
+ /**
825
+ * * Get current environment
826
+ * @return {Object} result
827
+ * @property {Boolean} reult.daemon whether or not daemon is running
828
+ * @property {Boolean} reult.environment Current environment
829
+ */
830
+ const target = async () => {
831
+ let daemon = false;
832
+ const env = await environment();
833
+ if (!https()) daemon = await hasDaemon();
834
+
835
+ return {daemon, environment: env}
1125
836
  };
1126
837
 
1127
- class DhtEarth {
1128
- /**
1129
- *
1130
- */
1131
- constructor() {
1132
- this.providerMap = new Map();
1133
- }
1134
-
1135
- /**
1136
- * @param {Object} address
1137
- * @return {Object} {latitude: lat, longitude: lon}
1138
- */
1139
- async getCoordinates(address) {
1140
- // const {address} = parseAddress(provider)
1141
- const request = `https://whereis.leofcoin.org/?ip=${address}`;
1142
- let response = await fetch__default["default"](request);
1143
- response = await response.json();
1144
- const {lat, lon} = response;
1145
- return {latitude: lat, longitude: lon}
1146
- }
1147
-
1148
- /**
1149
- * @param {Object} peer
1150
- * @param {Object} provider
1151
- * @return {Object} {provider, distance}
1152
- */
1153
- async getDistance(peer, provider) {
1154
- const {latitude, longitude} = await this.getCoordinates(provider.address);
1155
- return {provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude)}
1156
- }
1157
-
1158
- /**
1159
- * @param {Array} providers
1160
- * @return {Object} closestPeer
1161
- */
1162
- async closestPeer(providers) {
1163
- let all = [];
1164
- const address = await getAddress();
1165
- const peerLoc = await this.getCoordinates(address);
1166
-
1167
- for (const provider of providers) {
1168
- if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
1169
- else all.push(this.getDistance(peerLoc, provider));
1170
- }
1171
-
1172
- all = await Promise.all(all);
1173
- all = all.sort((previous, current) => previous.distance - current.distance);
1174
- return all[0].provider;
1175
- }
1176
-
1177
- /**
1178
- * @param {String} hash
1179
- * @return {Array} providers
1180
- */
1181
- providersFor(hash) {
1182
- return this.providerMap.get(hash);
1183
- }
1184
-
1185
- /**
1186
- * @param {String} address
1187
- * @param {String} hash
1188
- * @return {Array} providers
1189
- */
1190
- async addProvider(address, hash) {
1191
- let providers = [];
1192
- if (this.providerMap.has(hash)) providers = this.providerMap.get(hash);
1193
-
1194
- providers = new Set([...providers, address]);
1195
- this.providerMap.set(hash, providers);
1196
- return providers;
1197
- }
838
+ class PeerDiscovery {
839
+ constructor(id) {
840
+ this.id = id;
841
+ }
842
+
843
+ _getPeerId(id) {
844
+ if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
845
+
846
+ for (const entry of [...peernet.peerMap.entries()]) {
847
+ for (const _id of entry[1]) {
848
+ if (_id === id) return entry[0]
849
+ }
850
+ }
851
+ }
852
+
853
+ async discover(peer) {
854
+ let id = this._getPeerId(peer.id);
855
+ if (id) return id
856
+ const data = new peernet.protos['peernet-peer']({id: this.id});
857
+ const node = await peernet.prepareMessage(peer.id, data.encoded);
858
+
859
+ let response = await peer.request(node.encoded);
860
+ response = protoFor(response);
861
+ response = new peernet.protos['peernet-peer-response'](response.decoded.data);
862
+
863
+ id = response.decoded.id;
864
+ if (id === this.id) return;
865
+
866
+ if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
867
+ else {
868
+ const connections = peernet.peerMap.get(id);
869
+ if (connections.indexOf(peer.id) === -1) {
870
+ connections.push(peer.id);
871
+ peernet.peerMap.set(peer.id, connections);
872
+ }
873
+ }
874
+ return id
875
+ }
876
+
877
+ async discoverHandler(message, peer) {
878
+ const {id, proto} = message;
879
+ // if (typeof message.data === 'string') message.data = Buffer.from(message.data)
880
+ if (proto.name === 'peernet-peer') {
881
+ const from = proto.decoded.id;
882
+ if (from === this.id) return;
883
+
884
+ if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
885
+ else {
886
+ const connections = peernet.peerMap.get(from);
887
+ if (connections.indexOf(peer.id) === -1) {
888
+ connections.push(peer.id);
889
+ peernet.peerMap.set(from, connections);
890
+ }
891
+ }
892
+ const data = new peernet.protos['peernet-peer-response']({id: this.id});
893
+ const node = await peernet.prepareMessage(from, data.encoded);
894
+
895
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
896
+ } else if (proto.name === 'peernet-peer-response') {
897
+ const from = proto.decoded.id;
898
+ if (from === this.id) return;
899
+
900
+ if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
901
+ else {
902
+ const connections = peernet.peerMap.get(from);
903
+ if (connections.indexOf(peer.id) === -1) {
904
+ connections.push(peer.id);
905
+ peernet.peerMap.set(from, connections);
906
+ }
907
+ }
908
+ }
909
+ }
1198
910
  }
1199
911
 
1200
912
  /**
1201
- * @params {String} network
1202
- * @return {object} { identity, accounts, config }
913
+ * Keep history of fetched address and ptr
914
+ * @property {Object} address
915
+ * @property {Object} ptr
1203
916
  */
1204
- var generateAccount = async network => {
1205
- const wallet = new MultiWallet__default["default"](network);
1206
- /**
1207
- * @type {string}
1208
- */
1209
- const mnemonic = await wallet.generate();
1210
- /**
1211
- * @type {object}
1212
- */
1213
- const account = wallet.account(0);
1214
- /**
1215
- * @type {object}
1216
- */
1217
- const external = account.external(0);
1218
- const internal = account.internal(0);
1219
-
1220
- return {
1221
- identity: {
1222
- mnemonic,
1223
- multiWIF: wallet.export(),
1224
- publicKey: external.publicKey,
1225
- privateKey: external.privateKey,
1226
- walletId: external.id
1227
- },
1228
- accounts: [['main account', external.address, internal.address]],
1229
- config: {
1230
- miner: {
1231
- intensity: 1,
1232
- address: external.address,
1233
- donationAddress: undefined,
1234
- donationAmount: 1 //percent
1235
- }
1236
- }
917
+ const lastFetched = {
918
+ address: {
919
+ value: undefined,
920
+ timestamp: 0,
921
+ },
922
+ ptr: {
923
+ value: undefined,
924
+ timestamp: 0,
925
+ },
926
+ };
927
+
928
+ const getAddress = async () => {
929
+ const {address} = lastFetched;
930
+ const now = Math.round(new Date().getTime() / 1000);
931
+ if (now - address.timestamp > 1200000) {
932
+ address.value = await fetch__default["default"]('https://icanhazip.com/');
933
+ address.value = await address.value.text();
934
+ address.timestamp = Math.round(new Date().getTime() / 1000);
935
+ lastFetched.address = address;
936
+ }
937
+
938
+ return address.value
939
+ };
940
+
941
+ const degreesToRadians = (degrees) => {
942
+ return degrees * Math.PI / 180;
943
+ };
944
+
945
+ const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
946
+ const earthRadiusKm = 6371;
947
+
948
+ const dLat = degreesToRadians(lat2-lat1);
949
+ const dLon = degreesToRadians(lon2-lon1);
950
+
951
+ lat1 = degreesToRadians(lat1);
952
+ lat2 = degreesToRadians(lat2);
953
+ const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
954
+ Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
955
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
956
+ return earthRadiusKm * c;
957
+ };
958
+
959
+ class DhtEarth {
960
+ /**
961
+ *
962
+ */
963
+ constructor() {
964
+ this.providerMap = new Map();
965
+ }
966
+
967
+ /**
968
+ * @param {Object} address
969
+ * @return {Object} {latitude: lat, longitude: lon}
970
+ */
971
+ async getCoordinates(address) {
972
+ // const {address} = parseAddress(provider)
973
+ const request = `https://whereis.leofcoin.org/?ip=${address}`;
974
+ let response = await fetch__default["default"](request);
975
+ response = await response.json();
976
+ const {lat, lon} = response;
977
+ return {latitude: lat, longitude: lon}
978
+ }
979
+
980
+ /**
981
+ * @param {Object} peer
982
+ * @param {Object} provider
983
+ * @return {Object} {provider, distance}
984
+ */
985
+ async getDistance(peer, provider) {
986
+ const {latitude, longitude} = await this.getCoordinates(provider.address);
987
+ return {provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude)}
988
+ }
989
+
990
+ /**
991
+ * @param {Array} providers
992
+ * @return {Object} closestPeer
993
+ */
994
+ async closestPeer(providers) {
995
+ let all = [];
996
+ const address = await getAddress();
997
+ const peerLoc = await this.getCoordinates(address);
998
+
999
+ for (const provider of providers) {
1000
+ if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
1001
+ else all.push(this.getDistance(peerLoc, provider));
1237
1002
  }
1238
- };
1239
-
1240
- class MessageHandler {
1241
- constructor(network) {
1242
- this.network = network;
1243
- }
1244
- /**
1245
- * hash and sign message
1246
- *
1247
- * @param {object} message
1248
- * @param {Buffer} message.from peer id
1249
- * @param {Buffer} message.to peer id
1250
- * @param {string} message.data Peernet message
1251
- * (PeernetMessage excluded) encoded as a string
1252
- * @return signature
1253
- */
1254
- async hashAndSignMessage(message) {
1255
- const hasher = new hash(message, {name: 'peernet-message'});
1256
- const identity = await walletStore.get('identity');
1257
-
1258
- const wallet = new MultiWallet__default["default"](this.network);
1259
- wallet.import(identity.multiWIF);
1260
- return wallet.sign(hasher.hash.slice(0, 32))
1261
- }
1262
-
1263
- /**
1264
- * @param {String} from - peer id
1265
- * @param {String} to - peer id
1266
- * @param {String|PeernetMessage} data - data encoded message string
1267
- * or the messageNode itself
1268
- */
1269
- async prepareMessage(from, to, data) {
1270
- if (!Buffer.isBuffer(from)) from = new Buffer.from(from);
1271
- if (!Buffer.isBuffer(to)) to = new Buffer.from(to);
1272
- if (data.encoded) data = data.encoded;
1273
-
1274
- const message = {
1275
- from,
1276
- to,
1277
- data,
1278
- };
1279
- const signature = await this.hashAndSignMessage(message);
1280
- const node = new peernetMessage({
1281
- ...message,
1282
- signature,
1283
- });
1284
-
1285
- return node
1286
- }
1003
+
1004
+ all = await Promise.all(all);
1005
+ all = all.sort((previous, current) => previous.distance - current.distance);
1006
+ return all[0].provider;
1007
+ }
1008
+
1009
+ /**
1010
+ * @param {String} hash
1011
+ * @return {Array} providers
1012
+ */
1013
+ providersFor(hash) {
1014
+ return this.providerMap.get(hash);
1015
+ }
1016
+
1017
+ /**
1018
+ * @param {String} address
1019
+ * @param {String} hash
1020
+ * @return {Array} providers
1021
+ */
1022
+ async addProvider(address, hash) {
1023
+ let providers = [];
1024
+ if (this.providerMap.has(hash)) providers = this.providerMap.get(hash);
1025
+
1026
+ providers = new Set([...providers, address]);
1027
+ this.providerMap.set(hash, providers);
1028
+ return providers;
1029
+ }
1287
1030
  }
1288
1031
 
1289
- const encapsulatedError = () => {
1290
- return new Error('Nodes/Data should be send encapsulated by peernet-message')
1032
+ var testnets = {
1033
+ 'leofcoin:olivia': {
1034
+ messagePrefix: '\u0019Leofcoin Signed Message:',
1035
+ pubKeyHash: 0x73, // o
1036
+ scriptHash: 0x76, // p
1037
+ multiTxHash: 0x8b4125, // omtx
1038
+ payments: {
1039
+ version: 0,
1040
+ unspent: 0x1fa443d7 // ounsp
1041
+ },
1042
+ wif: 0x7D, // s
1043
+ multiCodec: 0x7c4,
1044
+ bip32: { public: 0x13BBF2D5, private: 0x13BBCBC5 }
1045
+ },
1046
+ 'bitcoin:testnet': {
1047
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
1048
+ bech32: 'tb',
1049
+ pubKeyHash: 0x6f,
1050
+ scriptHash: 0xc4,
1051
+ wif: 0xef,
1052
+ bip32: {
1053
+ public: 0x043587cf,
1054
+ private: 0x04358394
1055
+ }
1056
+ }
1057
+
1291
1058
  };
1292
1059
 
1293
- const dhtError = (proto) => {
1294
- const text = `Received proto ${proto.name} expected peernet-dht-response`;
1295
- return new Error(`Routing error: ${text}`)
1060
+ // https://en.bitcoin.it/wiki/List_of_address_prefixes
1061
+ /**
1062
+ * Main network
1063
+ * @return {messagePrefix, pubKeyHash, scriptHash, wif, bip32}
1064
+ */
1065
+ const leofcoin = {
1066
+ messagePrefix: '\u0019Leofcoin Signed Message:',
1067
+ pubKeyHash: 0x30, // L
1068
+ scriptHash: 0x37, // P
1069
+ multiTxHash: 0x3adeed, // Lmtx
1070
+ payments: {
1071
+ version: 0,
1072
+ unspent: 0x0d6e0327 // Lunsp
1073
+ },
1074
+ coin_type: 640,
1075
+ wif: 0x3F, // S
1076
+ multiCodec: 0x3c4,
1077
+ bip32: { public: 0x13BBF2D4, private: 0x13BBCBC4 },
1078
+ testnet: testnets['leofcoin:olivia']
1079
+ };
1080
+
1081
+ const bitcoin = {
1082
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
1083
+ bech32: 'bc',
1084
+ pubKeyHash: 0x00,
1085
+ scriptHash: 0x05,
1086
+ wif: 0x80,
1087
+ coin_type: 0,
1088
+ bip32: {
1089
+ public: 0x0488b21e, private: 0x0488ade4
1090
+ },
1091
+ testnet: testnets['bitcoin:testnet']
1092
+ };
1093
+
1094
+ const litecoin = {
1095
+ messagePrefix: '\x19Litecoin Signed Message:\n',
1096
+ pubKeyHash: 0x30,
1097
+ scriptHash: 0x32,
1098
+ wif: 0xb0,
1099
+ bip32: {
1100
+ public: 0x019da462,
1101
+ private: 0x019d9cfe
1102
+ }
1103
+ };
1104
+
1105
+ const ethereum = {
1106
+ messagePrefix: '\x19Ethereum Signed Message:\n',
1107
+ pubKeyHash: 0x30,
1108
+ scriptHash: 0x32,
1109
+ bip32: {
1110
+ private: 0x0488ADE4, public: 0x0488B21E
1111
+ },
1112
+ coin_type: 60,
1113
+ wif: 0x45,//E
1114
+ multiCodec: 0x3c5
1115
+ };
1116
+
1117
+ /**
1118
+ * Our & supported networks
1119
+ * @return {leofcoin, olivia}
1120
+ */
1121
+ var networks = {
1122
+ leofcoin,
1123
+ bitcoin,
1124
+ litecoin,
1125
+ ethereum
1296
1126
  };
1297
1127
 
1298
- const nothingFoundError = (hash) => {
1299
- return new Error(`nothing found for ${hash}`)
1128
+ const fromNetworkString = network => {
1129
+ const parts = network.split(':');
1130
+ network = networks[parts[0]];
1131
+ if (parts[1]) {
1132
+ if (network[parts[1]]) network = network[parts[1]];
1133
+
1134
+ network.coin_type = 1;
1135
+ }
1136
+ return network;
1300
1137
  };
1301
1138
 
1302
- globalThis.leofcoin = globalThis.leofcoin || {};
1303
- globalThis.globalSub = globalThis.globalSub || new PubSub__default["default"]({verbose: true});
1304
-
1305
- /**
1306
- * @access public
1307
- * @example
1308
- * const peernet = new Peernet();
1309
- */
1310
- class Peernet {
1311
- /**
1312
- * @access public
1313
- * @param {Object} options
1314
- * @param {String} options.network - desired network
1315
- * @param {String} options.root - path to root directory
1316
- * @param {String} options.storePrefix - prefix for datatores (lfc)
1317
- *
1318
- * @return {Promise} instance of Peernet
1319
- *
1320
- * @example
1321
- * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
1322
- */
1323
- constructor(options = {}) {
1324
- this._discovered = [];
1325
- /**
1326
- * @property {String} network - current network
1327
- */
1328
- this.network = options.network || 'leofcoin';
1329
- const parts = this.network.split(':');
1330
-
1331
- if (!options.storePrefix) options.storePrefix = 'lfc';
1332
- if (!options.port) options.port = 2000;
1333
- if (!options.root) {
1334
- if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
1335
- else options.root = `.${this.network}/peernet`;
1336
- }
1337
- globalThis.peernet = this;
1338
- this.bw = {
1339
- up: 0,
1340
- down: 0,
1341
- };
1342
- return this._init(options)
1343
- }
1344
-
1345
- get defaultStores() {
1346
- return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
1347
- }
1348
-
1349
- addProto(name, proto) {
1350
- if (!this.protos[name]) this.protos[name] = proto;
1351
- }
1352
-
1353
- addCodec(name, codec) {
1354
- if (!this.codecs[name]) this.codecs[name] = codec;
1355
- }
1356
-
1357
- async addStore(name, prefix, root, isPrivate = true) {
1358
- if (name === 'block' || name === 'transaction' || name === 'chain' ||
1359
- name === 'data' || name === 'message') isPrivate = false;
1360
-
1361
- let Storage;
1362
- if (this.hasDaemon) {
1363
- Storage = LeofcoinStorageClient;
1364
- } else {
1365
- Storage = LeofcoinStorage__default["default"];
1366
- }
1367
- globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
1368
- await new Storage(`${prefix}-${name}`, root);
1369
-
1370
- globalThis[`${name}Store`].private = isPrivate;
1371
- if (!isPrivate) this.stores.push(name);
1372
- }
1373
-
1374
-
1375
- /**
1376
- * @see MessageHandler
1377
- */
1378
- prepareMessage(to, data) {
1379
- return this._messageHandler.prepareMessage(this.id, to, data)
1380
- }
1381
-
1382
- /**
1383
- * @access public
1384
- *
1385
- * @return {Array} peerId
1386
- */
1387
- get peers() {
1388
- return [...connections.values()]
1389
- }
1139
+ const { encode: encode$1, decode: decode$1 } = bs58check__default["default"];
1140
+ class HDWallet {
1141
+
1142
+ get chainCodeBuffer() {
1143
+ return this.ifNotLocked(() => this.hdnode.chainCode)
1144
+ }
1145
+
1146
+ get chainCode() {
1147
+ return this.ifNotLocked(() => this.chainCodeBuffer.toString('hex'))
1148
+ }
1149
+
1150
+ get privateKeyBuffer() {
1151
+ return this.ifNotLocked(() => this.hdnode.privateKey)
1152
+ }
1153
+
1154
+ get privateKey() {
1155
+ return this.ifNotLocked(() => this.privateKeyBuffer.toString('hex'))
1156
+ }
1157
+
1158
+ get publicKeyBuffer() {
1159
+ return this.ifNotLocked(() => this.hdnode.publicKey)
1160
+ }
1161
+
1162
+ get publicKey() {
1163
+ return this.ifNotLocked(() => this.publicKeyBuffer.toString('hex'))
1164
+ }
1165
+
1166
+ get address() {
1167
+ // override testnet coin_type
1168
+ let coin_type = this.hdnode.network.coin_type;
1169
+ if (coin_type === 1) {
1170
+ if (this.networkName?.split(':')[0] === 'ethereum') coin_type = 60;
1171
+ if (this.networkName?.split(':')[0] === 'leofcoin') coin_type = 640;
1172
+ }
1173
+ if (coin_type === 60 || coin_type === 640) {
1174
+ let buffer = ecc__default["default"].pointFromScalar(this.hdnode.__D, false);
1175
+ buffer = Buffer.from(secp256k1.publicKeyConvert(buffer, false)).slice(1);
1176
+ let hash = createKeccakHash__default["default"]('keccak256').update(buffer).digest();
1177
+ return hash.slice(-20).toString('hex')
1178
+ }
1179
+ return encode$1(this.neutered.publicKeyBuffer)
1180
+ }
1181
+
1182
+ get accountAddress() {
1183
+ return this.ifNotLocked(() => encode$1(this.hdnode.publicKeyBuffer))
1184
+ }
1185
+
1186
+ get isTestnet() {
1187
+ if (typeof network === 'string')
1188
+ this.hdnode.network = fromNetworkString(network);
1189
+
1190
+ return Boolean(this.hdnode.network.coin_type === 1)
1191
+ }
1192
+
1193
+ constructor(network, hdnode) {
1194
+ if (typeof network === 'string') {
1195
+ this.networkName = network;
1196
+ this.network = fromNetworkString(network);
1197
+ } else if (typeof network === 'object')
1198
+ this.network = network;
1199
+
1200
+ if (hdnode) this.defineHDNode(hdnode);
1201
+ }
1202
+
1203
+ ifNotLocked(fn, params) {
1204
+ if (!this.locked) return fn(params);
1205
+ return null
1206
+ }
1207
+
1208
+ defineHDNode(value) {
1209
+ Object.defineProperty(this, 'hdnode', {
1210
+ configurable: false,
1211
+ writable: false,
1212
+ value: value
1213
+ });
1214
+ }
1215
+
1216
+ validateNetwork(network) {
1217
+ if (!network && !this.network) return console.error(`expected network to be defined`);
1218
+ if (!network && this.network) network = this.network;
1219
+ if (typeof network === 'string') network = fromNetworkString(network);
1220
+ if (typeof network !== 'object') return console.error('network not found');
1221
+ return network;
1222
+ }
1223
+
1224
+ async generate(network) {
1225
+ network = this.validateNetwork(network);
1226
+ const mnemonic = bip39.generateMnemonic();
1227
+ const seed = await bip39.mnemonicToSeed(mnemonic);
1228
+ this.defineHDNode(bip32__namespace.fromSeed(seed, network));
1229
+ return mnemonic; // userpw
1230
+ }
1231
+
1232
+ /**
1233
+ * recover using mnemonic (recovery word list)
1234
+ */
1235
+ async recover(mnemonic, network) {
1236
+ network = this.validateNetwork(network);
1237
+ const seed = await bip39.mnemonicToSeed(mnemonic);
1238
+ this.defineHDNode(bip32__namespace.fromSeed(seed, network));
1239
+ }
1240
+
1241
+ load(base58, network) {
1242
+ network = this.validateNetwork(network);
1243
+ this.defineHDNode(bip32__namespace.fromBase58(base58, network));
1244
+ }
1245
+
1246
+ save() {
1247
+ return this.hdnode.toBase58();
1248
+ }
1249
+
1250
+ fromAddress(address, chainCode, network) {
1251
+ network = this.validateNetwork(network);
1252
+ // if (network.coin_type === 60) {
1253
+ // address = Buffer.from(address, 'hex')
1254
+ // } else {
1255
+ address = decode$1(address);
1256
+ // }
1257
+
1258
+ if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = address.slice(1);
1259
+ this.defineHDNode(bip32__namespace.fromPublicKey(address, chainCode, network));
1260
+ }
1261
+
1262
+ fromPublicKey(hex, chainCode, network) {
1263
+ network = this.validateNetwork(network);
1264
+ if (!Buffer.isBuffer(hex)) hex = Buffer.from(hex, 'hex');
1265
+ if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = hex.slice(1);
1266
+ this.defineHDNode(bip32__namespace.fromPublicKey(hex, chainCode, network));
1267
+ }
1268
+ }
1390
1269
 
1391
- /**
1392
- * @private
1393
- *
1394
- * @param {Object} options
1395
- * @param {String} options.root - path to root directory
1396
- *
1397
- * @return {Promise} instance of Peernet
1398
- */
1399
- async _init(options) {
1400
- // peernetDHT aka closesPeer by coordinates
1401
- /**
1402
- * @type {Object}
1403
- * @property {Object} peer Instance of Peer
1404
- */
1405
- this.dht = new DhtEarth();
1406
- /**
1407
- * @type {Map}
1408
- * @property {Object} peer Instance of Peer
1409
- */
1410
- this.peerMap = new Map();
1411
- this.stores = [];
1412
- this.requestProtos = {};
1413
- this.storePrefix = options.storePrefix;
1414
- this.root = options.root;
1270
+ const { encode, decode } = bs58check__namespace;
1271
+
1272
+ // TODO: multihash addresses
1273
+ class HDAccount {
1274
+ /**
1275
+ * @param {number} depth - acount depth
1276
+ */
1277
+ constructor(node, depth = 0) {
1278
+ this.node = node;
1279
+ this.depth = depth;
1280
+ this._prefix = `m/44'/${node.network.coin_type}'/${depth}'/`;
1281
+ }
1282
+
1283
+ /**
1284
+ * @param {number} index - address index
1285
+ */
1286
+ internal(index = 0) {
1287
+ return this.node.derivePath(`${this._prefix}1/${index}`)
1288
+ }
1289
+
1290
+ /**
1291
+ * @param {number} index - address index
1292
+ */
1293
+ external(index = 0) {
1294
+ return this.node.derivePath(`${this._prefix}0/${index}`)
1295
+ }
1296
+ }
1297
+
1298
+ class MultiWallet extends HDWallet {
1299
+ constructor(network, hdnode) {
1300
+ const networkName = network;
1301
+ super(network, hdnode);
1302
+ if (typeof networkName === 'string') this.networkName = networkName;
1303
+ this.multiCodec = this.network.multiCodec;
1304
+ this.version = 0x00;
1305
+ }
1306
+
1307
+ get id() {
1308
+ const buffer = Buffer.concat([
1309
+ Buffer.from(varint__default["default"].encode(this.multiCodec)),
1310
+ Buffer.from(this.account(0).node.neutered.publicKey, 'hex')
1311
+ ]);
1312
+ return encode(buffer)
1313
+ }
1314
+
1315
+ get multiWIF() {
1316
+ return this.ifNotLocked(() => this.encode())
1317
+ }
1318
+
1319
+ get neutered() {
1320
+ const neutered = this.ifNotLocked(() => new MultiWallet(this.network, this.hdnode.neutered()));
1321
+ if (neutered) this._neutered = neutered;
1322
+ return this._neutered
1323
+ }
1324
+
1325
+ fromId(id) {
1326
+ let buffer = decode(id);
1327
+ varint__default["default"].decode(buffer);
1328
+ buffer = buffer.slice(varint__default["default"].decode.bytes);
1329
+ this.fromPublicKey(buffer, null, this.network);
1330
+ }
1331
+
1332
+ lock(key, multiWIF) {
1333
+ if (!multiWIF) multiWIF = this.multiWIF;
1334
+ this.encrypted = AES__default["default"].encrypt(multiWIF.toString('hex'), key).toString();
1335
+ this.locked = true;
1336
+ }
1337
+
1338
+ unlock(key, encrypted) {
1339
+ if (!encrypted) encrypted = this.encrypted;
1340
+ this.import(AES__default["default"].decrypt(encrypted, key).toString(ENC__default["default"]));
1341
+ this.locked = false;
1342
+ }
1343
+
1344
+ export() {
1345
+ return this.encode();
1346
+ }
1347
+
1348
+ /**
1349
+ * encodes the multiWIF and loads wallet from bs58
1350
+ *
1351
+ * @param {multiWIF} multiWIF - note a multiWIF is not the same as a wif
1352
+ */
1353
+ import(multiWIF) {
1354
+ const { bs58, version, multiCodec } = this.decode(multiWIF);
1355
+ this.network = Object.values(networks).reduce((p, c) => {
1356
+ if (c.multiCodec===multiCodec) return c
1357
+ else if (c.testnet && c.testnet.multiCodec === multiCodec) return c.testnet
1358
+ else return p
1359
+ }, networks['leofcoin']);
1360
+ this.load(bs58, this.network);
1361
+ }
1362
+
1363
+ /**
1364
+ * @return base58Check encoded string
1365
+ */
1366
+ encode() {
1367
+ const buffer = Buffer.concat([
1368
+ Buffer.from(varint__default["default"].encode(this.version)),
1369
+ Buffer.from(varint__default["default"].encode(this.multiCodec)),
1370
+ decode(this.save())
1371
+ ]);
1372
+ return encode(buffer);
1373
+ }
1374
+
1375
+ decode(bs58) {
1376
+ let buffer = decode(bs58);
1377
+ const version = varint__default["default"].decode(buffer);
1378
+ buffer = buffer.slice(varint__default["default"].decode.bytes);
1379
+ const multiCodec = varint__default["default"].decode(buffer);
1380
+ buffer = buffer.slice(varint__default["default"].decode.bytes);
1381
+ bs58 = encode(buffer);
1382
+ if (version !== this.version) throw TypeError('Invalid version');
1383
+ if (this.multiCodec !== multiCodec) throw TypeError('Invalid multiCodec');
1384
+ return { version, multiCodec, bs58 };
1385
+ }
1386
+
1387
+ sign(hash) {
1388
+ return new MultiSignature__default["default"](this.version, this.network.multiCodec)
1389
+ .sign(hash, this.privateKeyBuffer);
1390
+
1391
+ }
1392
+
1393
+ verify(multiSignature, hash) {
1394
+ return new MultiSignature__default["default"](this.version, this.network.multiCodec)
1395
+ .verify(multiSignature, hash, this.publicKeyBuffer)
1396
+ }
1397
+
1398
+ /**
1399
+ * @param {number} account - account to return chain for
1400
+ * @return { internal(addressIndex), external(addressIndex) }
1401
+ */
1402
+ account(index) {
1403
+ return new HDAccount(new MultiWallet(this.network, this.hdnode), index);
1404
+ }
1405
+
1406
+ /**
1407
+ * m / purpose' / coin_type' / account' / change / aadress_index
1408
+ *
1409
+ * see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
1410
+ */
1411
+ derivePath(path) {
1412
+ return new MultiWallet(this.network, this.hdnode.derivePath(path))
1413
+ }
1414
+
1415
+ derive(index) {
1416
+ return new MultiWallet(this.network, this.hdnode.derive(index));
1417
+ }
1418
+ }
1415
1419
 
1416
- /**
1417
- * proto Object containing protos
1418
- * @type {Object}
1419
- * @property {PeernetMessage} protos[peernet-message] messageNode
1420
- * @property {DHTMessage} protos[peernet-dht] messageNode
1421
- * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
1422
- * @property {DataMessage} protos[peernet-data] messageNode
1423
- * @property {DataMessageResponse} protos[peernet-data-response] messageNode
1424
- */
1425
- globalThis.peernet.protos = {
1426
- 'peernet-request': request,
1427
- 'peernet-response': response,
1428
- 'peernet-peer': PeerMessage,
1429
- 'peernet-peer-response': PeerMessageResponse,
1430
- 'peernet-message': peernetMessage,
1431
- 'peernet-dht': dht,
1432
- 'peernet-dht-response': dhtResponse,
1433
- 'peernet-data': DataMessage,
1434
- 'peernet-data-response': DataMessageResponse,
1435
- 'peernet-ps': PsMessage,
1436
- 'chat-message': ChatMessage,
1437
- };
1420
+ class MessageHandler {
1421
+ constructor(network) {
1422
+ this.network = network;
1423
+ }
1424
+ /**
1425
+ * hash and sign message
1426
+ *
1427
+ * @param {object} message
1428
+ * @param {Buffer} message.from peer id
1429
+ * @param {Buffer} message.to peer id
1430
+ * @param {string} message.data Peernet message
1431
+ * (PeernetMessage excluded) encoded as a string
1432
+ * @return signature
1433
+ */
1434
+ async hashAndSignMessage(message) {
1435
+ const hasher = new hash(message, {name: 'peernet-message'});
1436
+ const identity = await walletStore.get('identity');
1437
+
1438
+ const wallet = new MultiWallet(this.network);
1439
+ wallet.import(identity.multiWIF);
1440
+ return wallet.sign(hasher.hash.slice(0, 32))
1441
+ }
1442
+
1443
+ /**
1444
+ * @param {String} from - peer id
1445
+ * @param {String} to - peer id
1446
+ * @param {String|PeernetMessage} data - data encoded message string
1447
+ * or the messageNode itself
1448
+ */
1449
+ async prepareMessage(from, to, data, id) {
1450
+ if (data.encoded) data = data.encoded;
1451
+
1452
+ const message = {
1453
+ from,
1454
+ to,
1455
+ data,
1456
+ };
1457
+ const signature = await this.hashAndSignMessage(message);
1458
+ const node = new peernetMessage({
1459
+ ...message,
1460
+ signature,
1461
+ });
1462
+
1463
+ return node
1464
+ }
1465
+ }
1438
1466
 
1439
- this.protos = globalThis.peernet.protos;
1440
- this.codecs = codec.codecs;
1467
+ const encapsulatedError = () => {
1468
+ return new Error('Nodes/Data should be send encapsulated by peernet-message')
1469
+ };
1441
1470
 
1442
- this._messageHandler = new MessageHandler(this.network);
1471
+ const dhtError = (proto) => {
1472
+ const text = `Received proto ${proto.name} expected peernet-dht-response`;
1473
+ return new Error(`Routing error: ${text}`)
1474
+ };
1443
1475
 
1444
- const {daemon, environment} = await target();
1445
- this.hasDaemon = daemon;
1476
+ const nothingFoundError = (hash) => {
1477
+ return new Error(`nothing found for ${hash}`)
1478
+ };
1446
1479
 
1480
+ globalThis.leofcoin = globalThis.leofcoin || {};
1481
+ globalThis.globalSub = globalThis.globalSub || new PubSub({verbose: true});
1482
+
1483
+ /**
1484
+ * @access public
1485
+ * @example
1486
+ * const peernet = new Peernet();
1487
+ */
1488
+ class Peernet {
1489
+ /**
1490
+ * @access public
1491
+ * @param {Object} options
1492
+ * @param {String} options.network - desired network
1493
+ * @param {String} options.root - path to root directory
1494
+ * @param {String} options.storePrefix - prefix for datatores (lfc)
1495
+ *
1496
+ * @return {Promise} instance of Peernet
1497
+ *
1498
+ * @example
1499
+ * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
1500
+ */
1501
+ constructor(options = {}) {
1502
+ this._discovered = [];
1503
+ /**
1504
+ * @property {String} network - current network
1505
+ */
1506
+ this.network = options.network || 'leofcoin';
1507
+ const parts = this.network.split(':');
1508
+
1509
+ if (!options.storePrefix) options.storePrefix = 'lfc';
1510
+ if (!options.port) options.port = 2000;
1511
+ if (!options.root) {
1512
+ if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
1513
+ else options.root = `.${this.network}/peernet`;
1514
+ }
1515
+ globalThis.peernet = this;
1516
+ this.bw = {
1517
+ up: 0,
1518
+ down: 0,
1519
+ };
1520
+ return this._init(options)
1521
+ }
1522
+
1523
+ get defaultStores() {
1524
+ return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
1525
+ }
1526
+
1527
+ addProto(name, proto) {
1528
+ if (!this.protos[name]) this.protos[name] = proto;
1529
+ }
1530
+
1531
+ addCodec(name, codec) {
1532
+ if (!this.codecs[name]) this.codecs[name] = codec;
1533
+ }
1534
+
1535
+ async addStore(name, prefix, root, isPrivate = true) {
1536
+ if (name === 'block' || name === 'transaction' || name === 'chain' ||
1537
+ name === 'data' || name === 'message') isPrivate = false;
1538
+
1539
+ let Storage;
1540
+ if (this.hasDaemon) {
1541
+ Storage = LeofcoinStorageClient;
1542
+ } else {
1543
+ Storage = globalThis.LeofcoinStorage?.default ? globalThis.LeofcoinStorage.default : LeofcoinStorage__default["default"];
1544
+ }
1545
+ globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
1546
+ await new Storage(`${prefix}-${name}`, root);
1547
+
1548
+ globalThis[`${name}Store`].private = isPrivate;
1549
+ if (!isPrivate) this.stores.push(name);
1550
+ }
1551
+
1552
+
1553
+ /**
1554
+ * @see MessageHandler
1555
+ */
1556
+ prepareMessage(to, data) {
1557
+ return this._messageHandler.prepareMessage(this.id, to, data)
1558
+ }
1559
+
1560
+ /**
1561
+ * @access public
1562
+ *
1563
+ * @return {Array} peerId
1564
+ */
1565
+ get peers() {
1566
+ return [...connections.values()]
1567
+ }
1568
+
1569
+ /**
1570
+ * @private
1571
+ *
1572
+ * @param {Object} options
1573
+ * @param {String} options.root - path to root directory
1574
+ *
1575
+ * @return {Promise} instance of Peernet
1576
+ */
1577
+ async _init(options) {
1578
+ // peernetDHT aka closesPeer by coordinates
1579
+ /**
1580
+ * @type {Object}
1581
+ * @property {Object} peer Instance of Peer
1582
+ */
1583
+ this.dht = new DhtEarth();
1584
+ /**
1585
+ * @type {Map}
1586
+ * @property {Object} peer Instance of Peer
1587
+ */
1588
+ this.peerMap = new Map();
1589
+ this.stores = [];
1590
+ this.requestProtos = {};
1591
+ this.storePrefix = options.storePrefix;
1592
+ this.root = options.root;
1593
+
1594
+ /**
1595
+ * proto Object containing protos
1596
+ * @type {Object}
1597
+ * @property {PeernetMessage} protos[peernet-message] messageNode
1598
+ * @property {DHTMessage} protos[peernet-dht] messageNode
1599
+ * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
1600
+ * @property {DataMessage} protos[peernet-data] messageNode
1601
+ * @property {DataMessageResponse} protos[peernet-data-response] messageNode
1602
+ */
1603
+ globalThis.peernet.protos = {
1604
+ 'peernet-request': request,
1605
+ 'peernet-response': response,
1606
+ 'peernet-peer': PeerMessage,
1607
+ 'peernet-peer-response': PeerMessageResponse,
1608
+ 'peernet-message': peernetMessage,
1609
+ 'peernet-dht': dht,
1610
+ 'peernet-dht-response': dhtResponse,
1611
+ 'peernet-data': DataMessage,
1612
+ 'peernet-data-response': DataMessageResponse,
1613
+ 'peernet-ps': PsMessage,
1614
+ 'chat-message': ChatMessage,
1615
+ };
1616
+
1617
+ this.protos = globalThis.peernet.protos;
1618
+ this.codecs = codec.codecs;
1619
+
1620
+ this._messageHandler = new MessageHandler(this.network);
1621
+
1622
+ const {daemon, environment} = await target();
1623
+ this.hasDaemon = daemon;
1624
+
1447
1625
  if (this.hasDaemon) {
1448
- globalThis.peernet.client = await httpClient({
1449
- protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
1450
- });
1451
- } else {
1452
- if (environment !== 'browser') http(options);
1453
- }
1454
-
1455
- for (const store of this.defaultStores) {
1456
- await this.addStore(store, options.storePrefix, options.root);
1457
- }
1458
-
1459
- try {
1460
- const pub = await accountStore.get('public');
1461
- this.id = pub.walletId;
1462
- } catch (e) {
1463
- if (e.code === 'ERR_NOT_FOUND') {
1464
- const wallet = {};
1465
- const {identity, accounts, config} = await generateAccount(this.network);
1466
- wallet.identity = identity;
1467
- wallet.accounts = accounts;
1468
- wallet.version = 1;
1469
- walletStore.put(wallet);
1470
- await accountStore.put('config', config);
1471
- await accountStore.put('public', {walletId: wallet.identity.walletId});
1472
-
1473
- this.id = wallet.identity.walletId;
1474
- } else {
1475
- throw e
1476
- }
1477
- }
1478
- this._peerHandler = new PeerDiscovery(this.id);
1479
- // peernet id
1480
- const id = Buffer.from(this.id.slice(0, 32));
1481
- this.peerId = id;
1482
-
1483
- pubsub.subscribe('peer:discovered', async (peer) => {
1484
- peer.on('peernet.data', async (message) => {
1485
- const id = message.id;
1486
- message = new peernetMessage(Buffer.from(message.data.data));
1487
- const proto = protoFor(message.decoded.data);
1488
- await this._protoHandler({id, proto}, peer);
1489
- });
1490
- await this._peerHandler.discover(peer);
1491
- const fulldId = this._getPeerId(peer.id);
1492
- if (fulldId && this._discovered.indexOf(peer.id) === -1) {
1493
- this._discovered.push(peer.id);
1494
- pubsub.publish('peer:connected', peer);
1495
- }
1496
- });
1497
- pubsub.subscribe('peer:disconnected', async (peer) => {
1498
- let index = this._discovered.indexOf(peer.id);
1499
- if (index !== -1) this._discovered.splice(index, 1);
1500
- const id = this._getPeerId(peer.id);
1501
- let peerIds = this.peerMap.get(id);
1502
-
1503
- if (peerIds) {
1504
- index = peerIds.indexOf(peer.id);
1505
- if (index !== -1) peerIds.splice(index, 1);
1506
- } else {
1507
- peerIds = [];
1508
- }
1509
-
1510
- if (peerIds.length === 0) this.peerMap.delete(id);
1511
- else this.peerMap.set(id, peerIds);
1512
- });
1513
- pubsub.subscribe('peer:connected', async (peer) => {
1514
- console.log({connected: peer.id, as: this._getPeerId(peer.id) });
1515
- // peer.on('peernet.data', async (message) => {
1516
- // const id = message.id
1517
- // message = new PeernetMessage(Buffer.from(message.data.data))
1518
- // const proto = protoFor(message.decoded.data)
1519
- // this._protoHandler({id, proto}, peer)
1520
- // })
1521
- });
1522
-
1523
- /**
1524
- * @access public
1525
- * @type {PeernetClient}
1526
- */
1527
- this.client = new PeernetClient({...options, id});
1528
- if (globalThis.onbeforeunload) {
1529
- globalThis.addEventListener('beforeunload', async () => this.client.close());
1530
- }
1531
- return this
1532
- }
1533
-
1534
- _getPeerId(id) {
1535
- for (const entry of [...this.peerMap.entries()]) {
1536
- for (const _id of entry[1]) {
1537
- if (_id === id) return entry[0]
1538
- }
1539
- }
1540
- }
1541
-
1542
- addRequestHandler(name, method) {
1543
- this.requestProtos[name] = method;
1544
- }
1545
-
1546
- /**
1547
- * @private
1548
- *
1549
- * @param {Buffer} message - peernet message
1550
- * @param {PeernetPeer} peer - peernet peer
1551
- */
1552
- async _protoHandler(message, peer) {
1553
- const {id, proto} = message;
1554
- this.bw.down += proto.encoded.length;
1555
- if (proto.name === 'peernet-peer') {
1556
- const from = proto.decoded.id;
1557
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1558
- else {
1559
- const connections = this.peerMap.get(from);
1560
- if (connections.indexOf(peer.id) === -1) {
1561
- connections.push(peer.id);
1562
- this.peerMap.set(from, connections);
1563
- }
1564
- }
1565
- const data = new PeerMessageResponse({id: this.id});
1566
- const node = await this.prepareMessage(from, data.encoded);
1567
-
1568
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1569
- this.bw.up += node.encoded.length;
1570
- } else if (proto.name === 'peernet-peer-response') {
1571
- const from = proto.decoded.id;
1572
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1573
- else {
1574
- const connections = this.peerMap.get(from);
1575
- if (connections.indexOf(peer.id) === -1) {
1576
- connections.push(peer.id);
1577
- this.peerMap.set(from, connections);
1578
- }
1579
- }
1580
- } else {
1581
- let from = this._getPeerId(peer.id);
1582
- if (!from) {
1583
- const data = new PeerMessage({id: this.id});
1584
- const node = await this.prepareMessage(peer.id, data.encoded);
1585
- this.bw.up += node.encoded.length;
1586
- let response = await peer.request(node.encoded);
1587
- response = protoFor(response);
1588
-
1589
- response = new PeerMessageResponse(response.decoded.data);
1590
-
1591
- from = response.decoded.id;
1592
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1593
- else {
1594
- const connections = this.peerMap.get(from);
1595
- if (connections.indexOf(peer.id) === -1) {
1596
- connections.push(peer.id);
1597
- this.peerMap.set(from, connections);
1598
- }
1599
- }
1600
- }
1601
- if (proto.name === 'peernet-dht') {
1602
- let { hash, store } = proto.decoded;
1603
- let has;
1604
-
1605
- if (!store) {
1606
- has = await this.has(hash);
1607
- } else {
1608
- store = globalThis[`${store}Store`];
1609
- if (store.private) has = false;
1610
- else has = await store.has(hash);
1611
- }
1612
- const data = new dhtResponse({hash, has});
1613
- const node = await this.prepareMessage(from, data.encoded);
1614
-
1615
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1616
- this.bw.up += node.encoded.length;
1617
- } else if (proto.name === 'peernet-data') {
1618
- let { hash, store } = proto.decoded;
1619
- let data;
1620
- if (!store) {
1621
- store = await this.whichStore([...this.stores], hash);
1622
- } else {
1623
- store = globalThis[`${store}Store`];
1624
- }
1625
- if (store && !store.private) {
1626
- data = await store.get(hash);
1627
-
1628
- if (data) {
1629
- data = new DataMessageResponse({hash, data: data.decoded ? Buffer.from(JSON.stringify(data)) : Buffer.from(data)});
1630
-
1631
- const node = await this.prepareMessage(from, data.encoded);
1632
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1633
- this.bw.up += node.encoded.length;
1634
- }
1635
- }
1636
-
1637
- } else if (proto.name === 'peernet-peer') {
1638
- const from = proto.decoded.id;
1639
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1640
- else {
1641
- const connections = this.peerMap.get(from);
1642
- connections.push(peer.id);
1643
- this.peerMap.set(from, connections);
1644
- }
1645
- const data = new PeerMessage({id: this.id});
1646
- const node = await this.prepareMessage(from, data.encoded);
1647
-
1648
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1649
- this.bw.up += node.encoded.length;
1650
- } else if (proto.name === 'peernet-request') {
1651
- // TODO: make dynamic
1652
- // exposeddevapi[proto.decoded.request](proto.decoded.params)
1653
- const method = this.requestProtos[proto.decoded.request];
1654
- if (method) {
1655
- const data = await method();
1656
- const node = await this.prepareMessage(from, data.encoded);
1657
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1658
- this.bw.up += node.encoded.length;
1659
- }
1660
- } else if (proto.name === 'peernet-ps' &&
1661
- this._getPeerId(peer.id) !== this.id.toString()) {
1662
- globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
1663
- }
1664
- }
1665
- }
1666
-
1667
- /**
1668
- * performs a walk and resolves first encounter
1669
- *
1670
- * @param {String} hash
1671
- */
1672
- async walk(hash) {
1673
- if (!hash) throw new Error('hash expected, received undefined')
1674
- const data = new dht({hash});
1675
- this.client.id;
1676
- for (const peer of this.peers) {
1677
- const node = await this.prepareMessage(peer.id, data.encoded);
1678
-
1679
- const result = await peer.request(node.encoded);
1680
-
1681
- let proto = protoFor(result.data);
1682
-
1683
- if (proto.name !== 'peernet-message') throw encapsulatedError()
1684
- const from = proto.decoded.from;
1685
- proto = protoFor(proto.decoded.data);
1686
-
1687
- if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
1688
-
1689
- // TODO: give ip and port (just used for location)
1690
- if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
1691
- peer.connection.remoteFamily = 'ipv4';
1692
- peer.connection.remoteAddress = '127.0.0.1';
1693
- peer.connection.remotePort = '0000';
1694
- }
1695
-
1696
- const peerInfo = {
1697
- family: peer.connection.remoteFamily || peer.connection.localFamily,
1698
- address: peer.connection.remoteAddress || peer.connection.localAddress,
1699
- port: peer.connection.remotePort || peer.connection.localPort,
1700
- id: from,
1701
- };
1702
-
1703
- if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
1704
- }
1705
- return
1706
- }
1707
-
1708
- /**
1709
- * Override DHT behavior, try's finding the content three times
1710
- *
1711
- * @param {String} hash
1712
- */
1713
- async providersFor(hash) {
1714
- let providers = await this.dht.providersFor(hash);
1715
- // walk the network to find a provider
1716
- if (!providers || providers.length === 0) {
1717
- await this.walk(hash);
1718
- providers = await this.dht.providersFor(hash);
1719
- // second walk the network to find a provider
1720
- if (!providers || providers.length === 0) {
1721
- await this.walk(hash);
1722
- providers = await this.dht.providersFor(hash);
1723
- }
1724
- // last walk
1725
- if (!providers || providers.length === 0) {
1726
- await this.walk(hash);
1727
- providers = await this.dht.providersFor(hash);
1728
- }
1729
- }
1730
- // undefined if no providers given
1731
- return providers
1732
- }
1733
-
1734
- get block() {
1735
- return {
1736
- get: async (hash) => {
1737
- const data = await blockStore.has(hash);
1738
- if (data) return await blockStore.get(hash)
1739
- return this.requestData(hash, 'block')
1740
- },
1741
- put: async (hash, data) => {
1742
- if (await blockStore.has(hash)) return
1743
- return await blockStore.put(hash, data)
1744
- },
1745
- has: async (hash) => await blockStore.has(hash, 'block'),
1746
- }
1747
- }
1748
-
1749
- get transaction() {
1750
- return {
1751
- get: async (hash) => {
1752
- const data = await transactionStore.has(hash);
1753
- if (data) return await transactionStore.get(hash)
1754
- return this.requestData(hash, 'transaction')
1755
- },
1756
- put: async (hash, data) => {
1757
- if (await transactionStore.has(hash)) return
1758
- return await transactionStore.put(hash, data)
1759
- },
1760
- has: async (hash) => await transactionStore.has(hash),
1761
- }
1762
- }
1763
-
1764
- async requestData(hash, store) {
1765
- const providers = await this.providersFor(hash);
1766
- if (!providers || providers.size === 0) throw nothingFoundError(hash)
1767
- debug(`found ${providers.size} provider(s) for ${hash}`);
1768
- // get closest peer on earth
1769
- const closestPeer = await this.dht.closestPeer(providers);
1770
- // get peer instance by id
1771
- if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
1772
-
1773
- const id = closestPeer.id.toString();
1774
- if (this.peers) {
1775
- let closest = this.peers.filter((peer) => {
1776
- if (this._getPeerId(peer.id) === id) return peer
1777
- });
1778
-
1779
- let data = new DataMessage({hash, store: store.name ? store.name : store});
1780
-
1781
- const node = await this.prepareMessage(id, data.encoded);
1782
- if (closest[0]) data = await closest[0].request(node.encoded);
1783
- else {
1784
- closest = this.peers.filter((peer) => {
1785
- if (peer.id.toString() === id) return peer
1786
- });
1787
- if (closest[0]) data = await closest[0].request(node.encoded);
1788
- }
1789
- if (data.data) {
1790
- let proto = protoFor(Buffer.from(data.data));
1791
- proto = protoFor(proto.decoded.data);
1792
- return proto.decoded.data
1793
- }
1794
-
1795
- // this.put(hash, proto.decoded.data)
1796
- }
1797
- return null
1798
- }
1799
-
1800
-
1801
- get message() {
1802
- return {
1803
- /**
1804
- * Get content for given message hash
1805
- *
1806
- * @param {String} hash
1807
- */
1808
- get: async (hash) => {
1809
- debug(`get message ${hash}`);
1810
- const message = await messageStore.has(hash);
1811
- if (message) return await messageStore.get(hash)
1812
- return this.requestData(hash, 'message')
1813
- },
1814
- /**
1815
- * put message content
1816
- *
1817
- * @param {String} hash
1818
- * @param {Buffer} message
1819
- */
1820
- put: async (hash, message) => await messageStore.put(hash, message),
1821
- /**
1822
- * @param {String} hash
1823
- * @return {Boolean}
1824
- */
1825
- has: async (hash) => await messageStore.has(hash),
1826
- }
1827
- }
1828
-
1829
- get data() {
1830
- return {
1831
- /**
1832
- * Get content for given data hash
1833
- *
1834
- * @param {String} hash
1835
- */
1836
- get: async (hash) => {
1837
- debug(`get data ${hash}`);
1838
- const data = await dataStore.has(hash);
1839
- if (data) return await dataStore.get(hash)
1840
- return this.requestData(hash, 'data')
1841
- },
1842
- /**
1843
- * put data content
1844
- *
1845
- * @param {String} hash
1846
- * @param {Buffer} data
1847
- */
1848
- put: async (hash, data) => await dataStore.put(hash, data),
1849
- /**
1850
- * @param {String} hash
1851
- * @return {Boolean}
1852
- */
1853
- has: async (hash) => await dataStore.has(hash),
1854
- }
1855
- }
1856
-
1857
- /**
1858
- * goes trough given stores and tries to find data for given hash
1859
- * @param {Array} stores
1860
- * @param {string} hash
1861
- */
1862
- async whichStore(stores, hash) {
1863
- let store = stores.pop();
1864
- store = globalThis[`${store}Store`];
1865
- if (store) {
1866
- const has = await store.has(hash);
1867
- if (has) return store
1868
- if (stores.length > 0) return this.whichStore(stores, hash)
1869
- } else return null
1870
- }
1871
-
1872
- /**
1873
- * Get content for given hash
1874
- *
1875
- * @param {String} hash - the hash of the wanted data
1876
- * @param {String} store - storeName to access
1877
- */
1878
- async get(hash, store) {
1879
- debug(`get ${hash}`);
1880
- let data;
1881
- if (store) store = globalThis[`${store}Store`];
1882
- if (!store) store = await this.whichStore([...this.stores], hash);
1883
- if (store && await store.has(hash)) data = await store.get(hash);
1884
- if (data) return data
1885
-
1886
- return this.requestData(hash, store.name ? store.name : store)
1887
- }
1888
-
1889
- /**
1890
- * put content
1891
- *
1892
- * @param {String} hash
1893
- * @param {Buffer} data
1894
- * @param {String} store - storeName to access
1895
- */
1896
- async put(hash, data, store = 'data') {
1897
- store = globalThis[`${store}Store`];
1898
- return store.put(hash, data)
1899
- }
1900
-
1901
- /**
1902
- * @param {String} hash
1903
- * @return {Boolean}
1904
- */
1905
- async has(hash) {
1906
- const store = await this.whichStore([...this.stores], hash);
1907
- if (store) {
1908
- if (store.private) return false
1909
- else return true
1910
- }
1911
- return false
1912
- }
1913
-
1914
- /**
1915
- *
1916
- * @param {String} topic
1917
- * @param {String|Object|Array|Boolean|Buffer} data
1918
- */
1919
- async publish(topic, data) {
1920
- // globalSub.publish(topic, data)
1921
- if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
1922
- if (!Buffer.isBuffer(data)) data = Buffer.from(data);
1923
- const id = Math.random().toString(36).slice(-12);
1924
- data = new PsMessage({data, topic});
1925
- for (const peer of this.peers) {
1926
- if (peer.connection._connected) {
1927
- if (peer.id.toString() !== this.peerId.toString()) {
1928
- const node = await this.prepareMessage(peer.id, data.encoded);
1929
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1930
- }
1931
- } else {
1932
- this.removePeer(peer);
1933
- }
1934
- // TODO: if peer subscribed
1935
- }
1936
- }
1937
-
1938
- createHash(data, name) {
1939
- return new hash(data, {name})
1940
- }
1941
-
1942
- /**
1943
- *
1944
- * @param {String} topic
1945
- * @param {Method} cb
1946
- */
1947
- async subscribe(topic, cb) {
1948
- // TODO: if peer subscribed
1949
- globalSub.subscribe(topic, cb);
1950
- }
1951
-
1952
- async removePeer(peer) {
1953
- connections.delete(peer.id);
1954
- }
1955
-
1956
- get Buffer() {
1957
- return Buffer
1958
- }
1959
- // async block(index) {
1960
- // const _values = []
1961
- // for (const peer of this.peers) {
1962
- // const value = await peer.request({type: 'block', index})
1963
- // console.log(value);
1964
- // }
1965
- //
1966
- // }
1626
+ const httpClient = await Promise.resolve().then(function () { return require('./client-1a1f75e6.js'); });
1627
+ globalThis.peernet.client = await httpClient.default({
1628
+ protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port
1629
+ });
1630
+ } else {
1631
+ const http = await Promise.resolve().then(function () { return require('./http-4bc6caeb.js'); });
1632
+ if (environment !== 'browser') http.default(options);
1633
+ }
1634
+
1635
+ for (const store of this.defaultStores) {
1636
+ await this.addStore(store, options.storePrefix, options.root);
1637
+ }
1638
+
1639
+ try {
1640
+ const pub = await accountStore.get('public');
1641
+ this.id = pub.walletId;
1642
+ } catch (e) {
1643
+ if (e.code === 'ERR_NOT_FOUND') {
1644
+ const wallet = {};
1645
+ const {identity, accounts, config} = await generateAccount__default["default"](this.network);
1646
+ wallet.identity = identity;
1647
+ wallet.accounts = accounts;
1648
+ wallet.version = 1;
1649
+ walletStore.put(wallet);
1650
+ await accountStore.put('config', config);
1651
+ await accountStore.put('public', {walletId: wallet.identity.walletId});
1652
+
1653
+ this.id = wallet.identity.walletId;
1654
+ } else {
1655
+ throw e
1656
+ }
1657
+ }
1658
+ this._peerHandler = new PeerDiscovery(this.id);
1659
+ this.peerId = this.id;
1660
+
1661
+ pubsub.subscribe('peer:connected', async (peer) => {
1662
+ console.log(peer);
1663
+ // console.log({connected: peer.id, as: this._getPeerId(peer.id) });
1664
+ // peer.on('peernet.data', async (message) => {
1665
+ // const id = message.id
1666
+ // message = new PeernetMessage(Buffer.from(message.data.data))
1667
+ // const proto = protoFor(message.decoded.data)
1668
+ // this._protoHandler({id, proto}, peer)
1669
+ // })
1670
+ });
1671
+
1672
+ pubsub.subscribe('peer:data', async message => {
1673
+ if (!message.data) return
1674
+ const {id, data} = JSON.parse(new TextDecoder().decode(message.data));
1675
+ const uint8Array = new Uint8Array(Object.keys(data).length);
1676
+ for (var i = 0; i < Object.keys(data).length; i++) {
1677
+ uint8Array[i] = data[i];
1678
+ }
1679
+ message = new peernetMessage(uint8Array);
1680
+ const proto = protoFor(message.decoded.data);
1681
+
1682
+ const from = message.decoded.from;
1683
+ this._protoHandler({id, proto}, this.client.connections[from], from);
1684
+ });
1685
+
1686
+ /**
1687
+ * @access public
1688
+ * @type {PeernetClient}
1689
+ */
1690
+ this.client = new Client(this.id);
1691
+ if (globalThis.onbeforeunload) {
1692
+ globalThis.addEventListener('beforeunload', async () => this.client.close());
1693
+ }
1694
+ return this
1695
+ }
1696
+
1697
+ _getPeerId(id) {
1698
+ for (const entry of [...this.peerMap.entries()]) {
1699
+ for (const _id of entry[1]) {
1700
+ if (_id === id) return entry[0]
1701
+ }
1702
+ }
1703
+ }
1704
+
1705
+ addRequestHandler(name, method) {
1706
+ this.requestProtos[name] = method;
1707
+ }
1708
+
1709
+ /**
1710
+ * @private
1711
+ *
1712
+ * @param {Buffer} message - peernet message
1713
+ * @param {PeernetPeer} peer - peernet peer
1714
+ */
1715
+ async _protoHandler(message, peer, from) {
1716
+ const {id, proto} = message;
1717
+ this.bw.down += proto.encoded.length;
1718
+
1719
+ if (proto.name === 'peernet-dht') {
1720
+ let { hash, store } = proto.decoded;
1721
+ let has;
1722
+
1723
+ if (!store) {
1724
+ has = await this.has(hash);
1725
+ } else {
1726
+ store = globalThis[`${store}Store`];
1727
+ if (store.private) has = false;
1728
+ else has = await store.has(hash);
1729
+ }
1730
+ const data = new dhtResponse({hash, has});
1731
+ const node = await this.prepareMessage(from, data.encoded);
1732
+
1733
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
1734
+ this.bw.up += node.encoded.length;
1735
+ } else if (proto.name === 'peernet-data') {
1736
+ let { hash, store } = proto.decoded;
1737
+ let data;
1738
+ if (!store) {
1739
+ store = await this.whichStore([...this.stores], hash);
1740
+ } else {
1741
+ store = globalThis[`${store}Store`];
1742
+ }
1743
+ if (store && !store.private) {
1744
+ data = await store.get(hash);
1745
+
1746
+ if (data) {
1747
+ data = new DataMessageResponse({hash, data: data.decoded ? new TextEncoder().encode(JSON.stringify(data.decoded)) : data});
1748
+
1749
+ const node = await this.prepareMessage(from, data.encoded);
1750
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
1751
+ this.bw.up += node.encoded.length;
1752
+ }
1753
+ }
1754
+
1755
+ } else if (proto.name === 'peernet-request') {
1756
+ // TODO: make dynamic
1757
+ // exposeddevapi[proto.decoded.request](proto.decoded.params)
1758
+ const method = this.requestProtos[proto.decoded.request];
1759
+ if (method) {
1760
+ const data = await method();
1761
+ const node = await this.prepareMessage(from, data.encoded);
1762
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
1763
+
1764
+ this.bw.up += node.encoded.length;
1765
+ }
1766
+ } else if (proto.name === 'peernet-ps' &&
1767
+ this._getPeerId(peer.id) !== this.id.toString()) {
1768
+ globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
1769
+ }
1770
+ // }
1771
+ }
1772
+
1773
+ /**
1774
+ * performs a walk and resolves first encounter
1775
+ *
1776
+ * @param {String} hash
1777
+ */
1778
+ async walk(hash) {
1779
+ if (!hash) throw new Error('hash expected, received undefined')
1780
+ const data = new dht({hash});
1781
+ this.client.id;
1782
+ for (const peer of this.peers) {
1783
+ const node = await this.prepareMessage(peer.id, data.encoded);
1784
+
1785
+ const result = await peer.request(node.encoded);
1786
+
1787
+ let proto = protoFor(result.data);
1788
+
1789
+ if (proto.name !== 'peernet-message') throw encapsulatedError()
1790
+ const from = proto.decoded.from;
1791
+ proto = protoFor(proto.decoded.data);
1792
+
1793
+ if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
1794
+
1795
+ // TODO: give ip and port (just used for location)
1796
+ if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
1797
+ peer.connection.remoteFamily = 'ipv4';
1798
+ peer.connection.remoteAddress = '127.0.0.1';
1799
+ peer.connection.remotePort = '0000';
1800
+ }
1801
+
1802
+ const peerInfo = {
1803
+ family: peer.connection.remoteFamily || peer.connection.localFamily,
1804
+ address: peer.connection.remoteAddress || peer.connection.localAddress,
1805
+ port: peer.connection.remotePort || peer.connection.localPort,
1806
+ id: from,
1807
+ };
1808
+
1809
+ if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
1810
+ }
1811
+ return
1812
+ }
1813
+
1814
+ /**
1815
+ * Override DHT behavior, try's finding the content three times
1816
+ *
1817
+ * @param {String} hash
1818
+ */
1819
+ async providersFor(hash) {
1820
+ let providers = await this.dht.providersFor(hash);
1821
+ // walk the network to find a provider
1822
+ if (!providers || providers.length === 0) {
1823
+ await this.walk(hash);
1824
+ providers = await this.dht.providersFor(hash);
1825
+ // second walk the network to find a provider
1826
+ if (!providers || providers.length === 0) {
1827
+ await this.walk(hash);
1828
+ providers = await this.dht.providersFor(hash);
1829
+ }
1830
+ // last walk
1831
+ if (!providers || providers.length === 0) {
1832
+ await this.walk(hash);
1833
+ providers = await this.dht.providersFor(hash);
1834
+ }
1835
+ }
1836
+ // undefined if no providers given
1837
+ return providers
1838
+ }
1839
+
1840
+ get block() {
1841
+ return {
1842
+ get: async (hash) => {
1843
+ const data = await blockStore.has(hash);
1844
+ if (data) return await blockStore.get(hash)
1845
+ return this.requestData(hash, 'block')
1846
+ },
1847
+ put: async (hash, data) => {
1848
+ if (await blockStore.has(hash)) return
1849
+ return await blockStore.put(hash, data)
1850
+ },
1851
+ has: async (hash) => await blockStore.has(hash, 'block'),
1852
+ }
1853
+ }
1854
+
1855
+ get transaction() {
1856
+ return {
1857
+ get: async (hash) => {
1858
+ const data = await transactionStore.has(hash);
1859
+ if (data) return await transactionStore.get(hash)
1860
+ return this.requestData(hash, 'transaction')
1861
+ },
1862
+ put: async (hash, data) => {
1863
+ if (await transactionStore.has(hash)) return
1864
+ return await transactionStore.put(hash, data)
1865
+ },
1866
+ has: async (hash) => await transactionStore.has(hash),
1867
+ }
1868
+ }
1869
+
1870
+ async requestData(hash, store) {
1871
+ const providers = await this.providersFor(hash);
1872
+ if (!providers || providers.size === 0) throw nothingFoundError(hash)
1873
+ debug(`found ${providers.size} provider(s) for ${hash}`);
1874
+ // get closest peer on earth
1875
+ const closestPeer = await this.dht.closestPeer(providers);
1876
+ // get peer instance by id
1877
+ if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
1878
+
1879
+ const id = closestPeer.id.toString();
1880
+ if (this.peers) {
1881
+ let closest = this.peers.filter((peer) => {
1882
+ if (this._getPeerId(peer.id) === id) return peer
1883
+ });
1884
+
1885
+ let data = new DataMessage({hash, store: store.name ? store.name : store});
1886
+
1887
+ const node = await this.prepareMessage(id, data.encoded);
1888
+ if (closest[0]) data = await closest[0].request(node.encoded);
1889
+ else {
1890
+ closest = this.peers.filter((peer) => {
1891
+ if (peer.id.toString() === id) return peer
1892
+ });
1893
+ if (closest[0]) data = await closest[0].request(node.encoded);
1894
+ }
1895
+ if (data.data) {
1896
+ console.log(data.data);
1897
+ let proto = protoFor(data.data);
1898
+ proto = protoFor(proto.decoded.data);
1899
+ return proto.decoded.data
1900
+ }
1901
+
1902
+ // this.put(hash, proto.decoded.data)
1903
+ }
1904
+ return null
1905
+ }
1906
+
1907
+
1908
+ get message() {
1909
+ return {
1910
+ /**
1911
+ * Get content for given message hash
1912
+ *
1913
+ * @param {String} hash
1914
+ */
1915
+ get: async (hash) => {
1916
+ debug(`get message ${hash}`);
1917
+ const message = await messageStore.has(hash);
1918
+ if (message) return await messageStore.get(hash)
1919
+ return this.requestData(hash, 'message')
1920
+ },
1921
+ /**
1922
+ * put message content
1923
+ *
1924
+ * @param {String} hash
1925
+ * @param {Buffer} message
1926
+ */
1927
+ put: async (hash, message) => await messageStore.put(hash, message),
1928
+ /**
1929
+ * @param {String} hash
1930
+ * @return {Boolean}
1931
+ */
1932
+ has: async (hash) => await messageStore.has(hash),
1933
+ }
1934
+ }
1935
+
1936
+ get data() {
1937
+ return {
1938
+ /**
1939
+ * Get content for given data hash
1940
+ *
1941
+ * @param {String} hash
1942
+ */
1943
+ get: async (hash) => {
1944
+ debug(`get data ${hash}`);
1945
+ const data = await dataStore.has(hash);
1946
+ if (data) return await dataStore.get(hash)
1947
+ return this.requestData(hash, 'data')
1948
+ },
1949
+ /**
1950
+ * put data content
1951
+ *
1952
+ * @param {String} hash
1953
+ * @param {Buffer} data
1954
+ */
1955
+ put: async (hash, data) => await dataStore.put(hash, data),
1956
+ /**
1957
+ * @param {String} hash
1958
+ * @return {Boolean}
1959
+ */
1960
+ has: async (hash) => await dataStore.has(hash),
1961
+ }
1962
+ }
1963
+
1964
+ /**
1965
+ * goes trough given stores and tries to find data for given hash
1966
+ * @param {Array} stores
1967
+ * @param {string} hash
1968
+ */
1969
+ async whichStore(stores, hash) {
1970
+ let store = stores.pop();
1971
+ store = globalThis[`${store}Store`];
1972
+ if (store) {
1973
+ const has = await store.has(hash);
1974
+ if (has) return store
1975
+ if (stores.length > 0) return this.whichStore(stores, hash)
1976
+ } else return null
1977
+ }
1978
+
1979
+ /**
1980
+ * Get content for given hash
1981
+ *
1982
+ * @param {String} hash - the hash of the wanted data
1983
+ * @param {String} store - storeName to access
1984
+ */
1985
+ async get(hash, store) {
1986
+ debug(`get ${hash}`);
1987
+ let data;
1988
+ if (store) store = globalThis[`${store}Store`];
1989
+ if (!store) store = await this.whichStore([...this.stores], hash);
1990
+ if (store && await store.has(hash)) data = await store.get(hash);
1991
+ if (data) return data
1992
+
1993
+ return this.requestData(hash, store.name ? store.name : store)
1994
+ }
1995
+
1996
+ /**
1997
+ * put content
1998
+ *
1999
+ * @param {String} hash
2000
+ * @param {Buffer} data
2001
+ * @param {String} store - storeName to access
2002
+ */
2003
+ async put(hash, data, store = 'data') {
2004
+ store = globalThis[`${store}Store`];
2005
+ return store.put(hash, data)
2006
+ }
2007
+
2008
+ /**
2009
+ * @param {String} hash
2010
+ * @return {Boolean}
2011
+ */
2012
+ async has(hash) {
2013
+ const store = await this.whichStore([...this.stores], hash);
2014
+ if (store) {
2015
+ if (store.private) return false
2016
+ else return true
2017
+ }
2018
+ return false
2019
+ }
2020
+
2021
+ /**
2022
+ *
2023
+ * @param {String} topic
2024
+ * @param {String|Object|Array|Boolean|Buffer} data
2025
+ */
2026
+ async publish(topic, data) {
2027
+ // globalSub.publish(topic, data)
2028
+ if (topic instanceof Uint8Array === false) topic = new TextEncoder().encode(topic);
2029
+ if (data instanceof Uint8Array === false) data = new TextEncoder().encode(JSON.stringify(data));
2030
+ const id = Math.random().toString(36).slice(-12);
2031
+ data = new PsMessage({data, topic});
2032
+ for (const peer of this.peers) {
2033
+ if (peer.connection._connected) {
2034
+ if (peer.id.toString() !== this.peerId.toString()) {
2035
+ const node = await this.prepareMessage(peer.id, data.encoded);
2036
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
2037
+ }
2038
+ } else {
2039
+ this.removePeer(peer);
2040
+ }
2041
+ // TODO: if peer subscribed
2042
+ }
2043
+ }
2044
+
2045
+ createHash(data, name) {
2046
+ return new hash(data, {name})
2047
+ }
2048
+
2049
+ /**
2050
+ *
2051
+ * @param {String} topic
2052
+ * @param {Method} cb
2053
+ */
2054
+ async subscribe(topic, cb) {
2055
+ // TODO: if peer subscribed
2056
+ globalSub.subscribe(topic, cb);
2057
+ }
2058
+
2059
+ async removePeer(peer) {
2060
+ connections.delete(peer.id);
2061
+ }
2062
+
2063
+ get Buffer() {
2064
+ return Buffer
2065
+ }
2066
+ // async block(index) {
2067
+ // const _values = []
2068
+ // for (const peer of this.peers) {
2069
+ // const value = await peer.request({type: 'block', index})
2070
+ // console.log(value);
2071
+ // }
2072
+ //
2073
+ // }
1967
2074
  }
1968
2075
 
1969
2076
  module.exports = Peernet;