@leofcoin/peernet 0.11.31 → 0.12.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.
- package/dist/browser/generate-account.js +50 -0
- package/dist/browser/messages.js +439 -0
- package/dist/browser/pako.js +6731 -0
- package/dist/browser/peernet-swarm.js +836 -0
- package/dist/browser/peernet.js +7576 -54848
- package/dist/browser/protons.js +3786 -0
- package/dist/browser/storage.js +12 -0
- package/dist/browser/wrtc.js +28 -0
- package/dist/commonjs/dht-6a1b6246.js +45 -0
- package/dist/commonjs/dht-response-e4a603ea.js +34 -0
- package/dist/commonjs/dht-response.js +3 -24
- package/dist/commonjs/dht.js +3 -35
- package/dist/commonjs/messages-bcb02ee9.js +189 -0
- package/dist/commonjs/{peernet-message.js → peernet-6eef77d5.js} +12 -7
- package/dist/commonjs/peernet.js +60 -1368
- package/dist/commonjs/peernet2.js +8 -0
- package/dist/commonjs/request-95ed03ec.js +33 -0
- package/dist/commonjs/request.js +3 -23
- package/dist/commonjs/response-bae4e2a2.js +33 -0
- package/dist/commonjs/response.js +3 -23
- package/dist/module/messages-421f88db.js +323 -0
- package/dist/module/peernet.js +53 -1451
- package/package.json +7 -12
- package/rollup.config.js +2 -21
- package/src/discovery/peer-discovery.js +4 -4
- package/src/handlers/data.js +2 -2
- package/src/handlers/message.js +4 -5
- package/src/messages/{chat-message.js → chat.js} +5 -2
- package/src/messages/data-response.js +5 -2
- package/src/messages/data.js +5 -2
- package/src/messages/dht-response.js +5 -2
- package/src/messages/dht.js +5 -2
- package/src/messages/peer-response.js +5 -2
- package/src/messages/peer.js +5 -2
- package/src/messages/{peernet-message.js → peernet.js} +5 -2
- package/src/messages/ps.js +5 -2
- package/src/messages/request.js +5 -2
- package/src/messages/response.js +5 -2
- package/src/messages.js +11 -0
- package/src/peernet.js +45 -32
- package/webpack.config.js +6 -3
package/dist/module/peernet.js
CHANGED
|
@@ -1,910 +1,6 @@
|
|
|
1
1
|
import '@vandeurenglenn/debug';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import protons from 'protons';
|
|
5
|
-
import { FormatInterface, Codec, CodecHash, codecs } from '@leofcoin/codec-format-interface';
|
|
6
|
-
import MultiWallet$1 from '@leofcoin/multi-wallet';
|
|
7
|
-
import * as bs58check from 'bs58check';
|
|
8
|
-
import bs58check__default from 'bs58check';
|
|
9
|
-
import * as bip32 from 'bip32';
|
|
10
|
-
import createKeccakHash from 'keccak';
|
|
11
|
-
import ecc from 'tiny-secp256k1';
|
|
12
|
-
import Mnemonic from '@leofcoin/mnemonic';
|
|
13
|
-
import MultiSignature from 'multi-signature';
|
|
14
|
-
import varint from 'varint';
|
|
15
|
-
import randombytes from 'randombytes';
|
|
16
|
-
|
|
17
|
-
/* socket-request-client version 1.6.3 */
|
|
18
|
-
|
|
19
|
-
class LittlePubSub {
|
|
20
|
-
constructor(verbose = true) {
|
|
21
|
-
this.subscribers = {};
|
|
22
|
-
this.verbose = verbose;
|
|
23
|
-
}
|
|
24
|
-
subscribe(event, handler, context) {
|
|
25
|
-
if (typeof context === 'undefined') {
|
|
26
|
-
context = handler;
|
|
27
|
-
}
|
|
28
|
-
this.subscribers[event] = this.subscribers[event] || { handlers: [], value: null};
|
|
29
|
-
this.subscribers[event].handlers.push(handler.bind(context));
|
|
30
|
-
}
|
|
31
|
-
unsubscribe(event, handler, context) {
|
|
32
|
-
if (typeof context === 'undefined') {
|
|
33
|
-
context = handler;
|
|
34
|
-
}
|
|
35
|
-
if (this.subscribers[event]) {
|
|
36
|
-
const index = this.subscribers[event].handlers.indexOf(handler.bind(context));
|
|
37
|
-
this.subscribers[event].handlers.splice(index);
|
|
38
|
-
if (this.subscribers[event].handlers.length === 0) delete this.subscribers[event];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
publish(event, change) {
|
|
42
|
-
if (this.subscribers[event]) {
|
|
43
|
-
if (this.verbose || this.subscribers[event].value !== change) {
|
|
44
|
-
this.subscribers[event].value = change;
|
|
45
|
-
this.subscribers[event].handlers.forEach(handler => {
|
|
46
|
-
handler(change, this.subscribers[event].value);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
var clientApi = _pubsub => {
|
|
54
|
-
const subscribe = (topic, cb) => {
|
|
55
|
-
_pubsub.subscribe(topic, cb);
|
|
56
|
-
};
|
|
57
|
-
const unsubscribe = (topic, cb) => {
|
|
58
|
-
_pubsub.unsubscribe(topic, cb);
|
|
59
|
-
};
|
|
60
|
-
const publish = (topic, value) => {
|
|
61
|
-
_pubsub.publish(topic, value);
|
|
62
|
-
};
|
|
63
|
-
const _connectionState = (state) => {
|
|
64
|
-
switch (state) {
|
|
65
|
-
case 0:
|
|
66
|
-
return 'connecting'
|
|
67
|
-
case 1:
|
|
68
|
-
return 'open'
|
|
69
|
-
case 2:
|
|
70
|
-
return 'closing'
|
|
71
|
-
case 3:
|
|
72
|
-
return 'closed'
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
const request = (client, request) => {
|
|
76
|
-
return new Promise((resolve, reject) => {
|
|
77
|
-
const state = _connectionState(client.readyState);
|
|
78
|
-
if (state !== 'open') return reject(`coudn't send request to ${client.id}, no open connection found.`)
|
|
79
|
-
request.id = Math.random().toString(36).slice(-12);
|
|
80
|
-
const handler = result => {
|
|
81
|
-
if (result && result.error) return reject(result.error)
|
|
82
|
-
resolve({result, id: request.id, handler});
|
|
83
|
-
unsubscribe(request.id, handler);
|
|
84
|
-
};
|
|
85
|
-
subscribe(request.id, handler);
|
|
86
|
-
send(client, request);
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
const send = async (client, request) => {
|
|
90
|
-
return client.send(JSON.stringify(request))
|
|
91
|
-
};
|
|
92
|
-
const pubsub = client => {
|
|
93
|
-
return {
|
|
94
|
-
publish: (topic = 'pubsub', value) => {
|
|
95
|
-
return send(client, {url: 'pubsub', params: { topic, value }})
|
|
96
|
-
},
|
|
97
|
-
subscribe: (topic = 'pubsub', cb) => {
|
|
98
|
-
subscribe(topic, cb);
|
|
99
|
-
return send(client, {url: 'pubsub', params: { topic, subscribe: true }})
|
|
100
|
-
},
|
|
101
|
-
unsubscribe: (topic = 'pubsub', cb) => {
|
|
102
|
-
unsubscribe(topic, cb);
|
|
103
|
-
return send(client, {url: 'pubsub', params: { topic, unsubscribe: true }})
|
|
104
|
-
},
|
|
105
|
-
subscribers: _pubsub.subscribers
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const server = (client) => {
|
|
109
|
-
return {
|
|
110
|
-
uptime: async () => {
|
|
111
|
-
try {
|
|
112
|
-
const { result, id, handler } = await request(client, {url: 'uptime'});
|
|
113
|
-
unsubscribe(id, handler);
|
|
114
|
-
return result
|
|
115
|
-
} catch (e) {
|
|
116
|
-
throw e
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
ping: async () => {
|
|
120
|
-
try {
|
|
121
|
-
const now = new Date().getTime();
|
|
122
|
-
const { result, id, handler } = await request(client, {url: 'ping'});
|
|
123
|
-
unsubscribe(id, handler);
|
|
124
|
-
return (Number(result) - now)
|
|
125
|
-
} catch (e) {
|
|
126
|
-
throw e
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
const peernet = (client) => {
|
|
132
|
-
return {
|
|
133
|
-
join: async (params) => {
|
|
134
|
-
try {
|
|
135
|
-
params.join = true;
|
|
136
|
-
const requested = { url: 'peernet', params };
|
|
137
|
-
const { result, id, handler } = await request(client, requested);
|
|
138
|
-
unsubscribe(id, handler);
|
|
139
|
-
return result
|
|
140
|
-
} catch (e) {
|
|
141
|
-
throw e
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
leave: async (params) => {
|
|
145
|
-
try {
|
|
146
|
-
params.join = false;
|
|
147
|
-
const requested = { url: 'peernet', params };
|
|
148
|
-
const { result, id, handler } = await request(client, requested);
|
|
149
|
-
unsubscribe(id, handler);
|
|
150
|
-
return result
|
|
151
|
-
} catch (e) {
|
|
152
|
-
throw e
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
return { send, request, pubsub, server, subscribe, unsubscribe, publish, peernet }
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
if (!globalThis.PubSub) globalThis.PubSub = LittlePubSub;
|
|
161
|
-
if (!globalThis.pubsub) globalThis.pubsub = new LittlePubSub({verbose: false});
|
|
162
|
-
const socketRequestClient = (url, protocols = 'echo-protocol', options = { retry: false }) => {
|
|
163
|
-
const { retry } = options;
|
|
164
|
-
const api = clientApi(pubsub);
|
|
165
|
-
let tries = 0;
|
|
166
|
-
const onerror = error => {
|
|
167
|
-
if (pubsub.subscribers['error']) {
|
|
168
|
-
pubsub.publish('error', error);
|
|
169
|
-
} else {
|
|
170
|
-
console.error(error);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
const onmessage = message => {
|
|
174
|
-
const {value, url, status, id} = JSON.parse(message.data.toString());
|
|
175
|
-
const publisher = id ? id : url;
|
|
176
|
-
if (status === 200) {
|
|
177
|
-
pubsub.publish(publisher, value);
|
|
178
|
-
} else {
|
|
179
|
-
pubsub.publish(publisher, {error: value});
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
const clientConnection = client => {
|
|
183
|
-
const startTime = new Date().getTime();
|
|
184
|
-
return {
|
|
185
|
-
client,
|
|
186
|
-
request: async req => {
|
|
187
|
-
const { result, id, handler } = await api.request(client, req);
|
|
188
|
-
pubsub.unsubscribe(id, handler);
|
|
189
|
-
return result
|
|
190
|
-
},
|
|
191
|
-
send: req => api.send(client, req),
|
|
192
|
-
subscribe: api.subscribe,
|
|
193
|
-
unsubscribe: api.unsubscribe,
|
|
194
|
-
subscribers: api.subscribers,
|
|
195
|
-
publish: api.publish,
|
|
196
|
-
pubsub: api.pubsub(client),
|
|
197
|
-
uptime: () => {
|
|
198
|
-
const now = new Date().getTime();
|
|
199
|
-
return (now - startTime)
|
|
200
|
-
},
|
|
201
|
-
peernet: api.peernet(client),
|
|
202
|
-
server: api.server(client),
|
|
203
|
-
close: exit => {
|
|
204
|
-
client.close();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
return new Promise((resolve, reject) => {
|
|
209
|
-
const init = () => {
|
|
210
|
-
let ws;
|
|
211
|
-
if (typeof process === 'object' && !globalThis.WebSocket) {
|
|
212
|
-
ws = require('websocket').w3cwebsocket;
|
|
213
|
-
} else {
|
|
214
|
-
ws = WebSocket;
|
|
215
|
-
}
|
|
216
|
-
const client = new ws(url, protocols);
|
|
217
|
-
client.onmessage = onmessage;
|
|
218
|
-
client.onerror = onerror;
|
|
219
|
-
client.onopen = () => {
|
|
220
|
-
tries = 0;
|
|
221
|
-
resolve(clientConnection(client));
|
|
222
|
-
};
|
|
223
|
-
client.onclose = message => {
|
|
224
|
-
tries++;
|
|
225
|
-
if (!retry) return reject(options)
|
|
226
|
-
if (tries > 5) {
|
|
227
|
-
console.log(`${protocols} Client Closed`);
|
|
228
|
-
console.error(`could not connect to - ${url}/`);
|
|
229
|
-
return resolve(clientConnection(client))
|
|
230
|
-
}
|
|
231
|
-
if (message.code === 1006) {
|
|
232
|
-
console.log('Retrying in 10 seconds');
|
|
233
|
-
setTimeout(() => {
|
|
234
|
-
return init();
|
|
235
|
-
}, retry);
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
return init();
|
|
240
|
-
});
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
class Peer {
|
|
244
|
-
#connection
|
|
245
|
-
#connecting = false
|
|
246
|
-
#connected = false
|
|
247
|
-
#channelReady = false
|
|
248
|
-
#destroying = false
|
|
249
|
-
#destroyed = false
|
|
250
|
-
#isNegotiating = false
|
|
251
|
-
#firstNegotiation = true
|
|
252
|
-
#iceComplete = false
|
|
253
|
-
#remoteTracks = []
|
|
254
|
-
#remoteStreams = []
|
|
255
|
-
#pendingCandidates = []
|
|
256
|
-
#senderMap = new Map()
|
|
257
|
-
#messageQue = []
|
|
258
|
-
#chunksQue = {}
|
|
259
|
-
#iceCompleteTimer
|
|
260
|
-
#channel
|
|
261
|
-
#peerId
|
|
262
|
-
#chunkSize = 16 * 1024 // 16384
|
|
263
|
-
#queRunning = false
|
|
264
|
-
#MAX_BUFFERED_AMOUNT = 16 * 1024 * 1024
|
|
265
|
-
|
|
266
|
-
get connection() {
|
|
267
|
-
return this.#connection
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
get connected() {
|
|
271
|
-
return this.#connected
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
get readyState() {
|
|
275
|
-
return this.channel?.readyState
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* @params {Object} options
|
|
280
|
-
* @params {string} options.channelName - this peerid : otherpeer id
|
|
281
|
-
*/
|
|
282
|
-
constructor(options = {}) {
|
|
283
|
-
this._in = this._in.bind(this);
|
|
284
|
-
this.offerOptions = options.offerOptions;
|
|
285
|
-
this.initiator = options.initiator;
|
|
286
|
-
this.streams = options.streams;
|
|
287
|
-
this.socketClient = options.socketClient;
|
|
288
|
-
this.id = options.id;
|
|
289
|
-
this.to = options.to;
|
|
290
|
-
this.bw = {
|
|
291
|
-
up: 0,
|
|
292
|
-
down: 0
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
this.channelName = options.channelName;
|
|
296
|
-
|
|
297
|
-
this.#peerId = options.peerId;
|
|
298
|
-
this.options = options;
|
|
299
|
-
this.#init();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
get peerId() {
|
|
303
|
-
return this.#peerId
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
set socketClient(value) {
|
|
307
|
-
// this.socketClient?.pubsub.unsubscribe('signal', this._in)
|
|
308
|
-
this._socketClient = value;
|
|
309
|
-
this._socketClient.pubsub.subscribe('signal', this._in);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
get socketClient() {
|
|
313
|
-
return this._socketClient
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
splitMessage(message) {
|
|
317
|
-
const chunks = [];
|
|
318
|
-
message = pako.deflate(message);
|
|
319
|
-
const size = message.byteLength || message.length;
|
|
320
|
-
let offset = 0;
|
|
321
|
-
return new Promise((resolve, reject) => {
|
|
322
|
-
const splitMessage = () => {
|
|
323
|
-
const chunk = message.slice(offset, offset + this.#chunkSize > size ? size : offset + this.#chunkSize);
|
|
324
|
-
offset += this.#chunkSize;
|
|
325
|
-
chunks.push(chunk);
|
|
326
|
-
if (offset < size) return splitMessage()
|
|
327
|
-
else resolve({chunks, size});
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
splitMessage();
|
|
331
|
-
})
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
async #runQue() {
|
|
335
|
-
this.#queRunning = true;
|
|
336
|
-
if (this.#messageQue.length > 0 && this.channel.bufferedAmount + this.#messageQue[0]?.length < this.#MAX_BUFFERED_AMOUNT) {
|
|
337
|
-
const message = this.#messageQue.shift();
|
|
338
|
-
|
|
339
|
-
switch (this.channel?.readyState) {
|
|
340
|
-
case 'open':
|
|
341
|
-
await this.channel.send(message);
|
|
342
|
-
if (this.#messageQue.length > 0) return this.#runQue()
|
|
343
|
-
else this.#queRunning = false;
|
|
344
|
-
break;
|
|
345
|
-
case 'closed':
|
|
346
|
-
case 'closing':
|
|
347
|
-
this.#messageQue = [];
|
|
348
|
-
this.#queRunning = false;
|
|
349
|
-
debug('channel already closed, this usually means a bad implementation, try checking the readyState or check if the peer is connected before sending');
|
|
350
|
-
break;
|
|
351
|
-
case undefined:
|
|
352
|
-
this.#messageQue = [];
|
|
353
|
-
this.#queRunning = false;
|
|
354
|
-
debug(`trying to send before a channel is created`);
|
|
355
|
-
break;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
} else {
|
|
360
|
-
return setTimeout(() => this.#runQue(), 50)
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
#trySend({ size, id, chunks }) {
|
|
365
|
-
let offset = 0;
|
|
366
|
-
|
|
367
|
-
for (const chunk of chunks) {
|
|
368
|
-
const start = offset;
|
|
369
|
-
const end = offset + chunk.length;
|
|
370
|
-
|
|
371
|
-
const message = new TextEncoder().encode(JSON.stringify({ size, id, chunk, start, end }));
|
|
372
|
-
this.#messageQue.push(message);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (!this.queRunning) return this.#runQue()
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
async send(message, id) {
|
|
379
|
-
const { chunks, size } = await this.splitMessage(message);
|
|
380
|
-
return this.#trySend({ size, id, chunks })
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
request(data) {
|
|
384
|
-
return new Promise((resolve, reject) => {
|
|
385
|
-
const id = Math.random().toString(36).slice(-12);
|
|
386
|
-
|
|
387
|
-
const _onData = message => {
|
|
388
|
-
if (message.id === id) {
|
|
389
|
-
resolve(message.data);
|
|
390
|
-
pubsub.unsubscribe(`peer:data`, _onData);
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
pubsub.subscribe(`peer:data`, _onData);
|
|
395
|
-
|
|
396
|
-
// cleanup subscriptions
|
|
397
|
-
// setTimeout(() => {
|
|
398
|
-
// pubsub.unsubscribe(`peer:data-request-${id}`, _onData)
|
|
399
|
-
// }, 5000);
|
|
400
|
-
|
|
401
|
-
this.send(data, id);
|
|
402
|
-
})
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
async #init() {
|
|
406
|
-
try {
|
|
407
|
-
const iceServers = [{
|
|
408
|
-
urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
|
|
409
|
-
}, {
|
|
410
|
-
urls: "stun:openrelay.metered.ca:80",
|
|
411
|
-
}, {
|
|
412
|
-
urls: "turn:openrelay.metered.ca:443",
|
|
413
|
-
username: "openrelayproject",
|
|
414
|
-
credential: "openrelayproject",
|
|
415
|
-
}, {
|
|
416
|
-
urls: "turn:openrelay.metered.ca:443?transport=tcp",
|
|
417
|
-
username: "openrelayproject",
|
|
418
|
-
credential: "openrelayproject",
|
|
419
|
-
}];
|
|
420
|
-
|
|
421
|
-
this.#connection = new wrtc.RTCPeerConnection({iceServers});
|
|
422
|
-
|
|
423
|
-
this.#connection.onicecandidate = ({ candidate }) => {
|
|
424
|
-
if (candidate) {
|
|
425
|
-
this.address = candidate.address;
|
|
426
|
-
this.port = candidate.port;
|
|
427
|
-
this.protocol = candidate.protocol;
|
|
428
|
-
this.ipFamily = this.address.includes('::') ? 'ipv6': 'ipv4';
|
|
429
|
-
this._sendMessage({candidate});
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
// if (this.initiator) this.#connection.onnegotiationneeded = () => {
|
|
433
|
-
// console.log('create offer');
|
|
434
|
-
this.#connection.ondatachannel = (message) => {
|
|
435
|
-
message.channel.onopen = () => {
|
|
436
|
-
this.#connected = true;
|
|
437
|
-
pubsub.publish('peer:connected', this);
|
|
438
|
-
};
|
|
439
|
-
message.channel.onclose = () => this.close.bind(this);
|
|
440
|
-
|
|
441
|
-
message.channel.onmessage = (message) => {
|
|
442
|
-
this._handleMessage(this.id, message);
|
|
443
|
-
};
|
|
444
|
-
this.channel = message.channel;
|
|
445
|
-
};
|
|
446
|
-
if (this.initiator) {
|
|
447
|
-
|
|
448
|
-
this.channel = this.#connection.createDataChannel('messageChannel');
|
|
449
|
-
this.channel.onopen = () => {
|
|
450
|
-
this.#connected = true;
|
|
451
|
-
pubsub.publish('peer:connected', this);
|
|
452
|
-
// this.channel.send('hi')
|
|
453
|
-
};
|
|
454
|
-
this.channel.onclose = () => this.close.bind(this);
|
|
455
|
-
|
|
456
|
-
this.channel.onmessage = (message) => {
|
|
457
|
-
this._handleMessage(this.peerId, message);
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
const offer = await this.#connection.createOffer();
|
|
461
|
-
await this.#connection.setLocalDescription(offer);
|
|
462
|
-
|
|
463
|
-
this._sendMessage({'sdp': this.#connection.localDescription});
|
|
464
|
-
}
|
|
465
|
-
} catch (e) {
|
|
466
|
-
console.log(e);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
_handleMessage(peerId, message) {
|
|
471
|
-
debug(`incoming message from ${peerId}`);
|
|
472
|
-
|
|
473
|
-
message = JSON.parse(new TextDecoder().decode(message.data));
|
|
474
|
-
// allow sharding (multiple peers share data)
|
|
475
|
-
pubsub.publish('peernet:shard', message);
|
|
476
|
-
const { id } = message;
|
|
477
|
-
|
|
478
|
-
if (!this.#chunksQue[id]) this.#chunksQue[id] = [];
|
|
479
|
-
|
|
480
|
-
if (message.size > this.#chunksQue[id].length || message.size === this.#chunksQue[id].length) {
|
|
481
|
-
for (const value of Object.values(message.chunk)) {
|
|
482
|
-
this.#chunksQue[id].push(value);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (message.size === this.#chunksQue[id].length) {
|
|
487
|
-
let data = new Uint8Array(Object.values(this.#chunksQue[id]));
|
|
488
|
-
delete this.#chunksQue[id];
|
|
489
|
-
data = pako.inflate(data);
|
|
490
|
-
pubsub.publish('peer:data', { id, data });
|
|
491
|
-
}
|
|
492
|
-
this.bw.down += message.byteLength || message.length;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
_sendMessage(message) {
|
|
496
|
-
this.socketClient.send({url: 'signal', params: {
|
|
497
|
-
to: this.to,
|
|
498
|
-
from: this.id,
|
|
499
|
-
channelName: this.options.channelName,
|
|
500
|
-
...message
|
|
501
|
-
}});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
async _in(message, data) {
|
|
505
|
-
// message = JSON.parse(message);
|
|
506
|
-
if (message.to !== this.id) return
|
|
507
|
-
// if (data.videocall) return this._startStream(true, false); // start video and audio stream
|
|
508
|
-
// if (data.call) return this._startStream(true, true); // start audio stream
|
|
509
|
-
if (message.candidate) {
|
|
510
|
-
debug(`incoming candidate ${this.channelName}`);
|
|
511
|
-
debug(message.candidate.candidate);
|
|
512
|
-
this.remoteAddress = message.candidate.address;
|
|
513
|
-
this.remotePort = message.candidate.port;
|
|
514
|
-
this.remoteProtocol = message.candidate.protocol;
|
|
515
|
-
this.remoteIpFamily = this.remoteAddress?.includes('::') ? 'ipv6': 'ipv4';
|
|
516
|
-
return this.#connection.addIceCandidate(new wrtc.RTCIceCandidate(message.candidate));
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
if (message.sdp) {
|
|
520
|
-
if (message.sdp.type === 'offer') {
|
|
521
|
-
debug(`incoming offer ${this.channelName}`);
|
|
522
|
-
await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
|
|
523
|
-
const answer = await this.#connection.createAnswer();
|
|
524
|
-
await this.#connection.setLocalDescription(answer);
|
|
525
|
-
this._sendMessage({'sdp': this.#connection.localDescription});
|
|
526
|
-
}
|
|
527
|
-
if (message.sdp.type === 'answer') {
|
|
528
|
-
debug(`incoming answer ${this.channelName}`);
|
|
529
|
-
await this.#connection.setRemoteDescription(new wrtc.RTCSessionDescription(message.sdp));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
} catch (e) {
|
|
533
|
-
console.log(e);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
close() {
|
|
538
|
-
debug(`closing ${this.peerId}`);
|
|
539
|
-
this.#connected = false;
|
|
540
|
-
this.channel?.close();
|
|
541
|
-
this.#connection?.close();
|
|
542
|
-
|
|
543
|
-
this.socketClient.pubsub.unsubscribe('signal', this._in);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
class Client {
|
|
548
|
-
#peerConnection
|
|
549
|
-
#connections = {}
|
|
550
|
-
#stars = {}
|
|
551
|
-
|
|
552
|
-
get connections() {
|
|
553
|
-
return { ...this.#connections }
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
get peers() {
|
|
557
|
-
return Object.entries(this.#connections)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
constructor(id, identifiers = ['peernet-v0.1.0'], stars = []) {
|
|
561
|
-
this.id = id || Math.random().toString(36).slice(-12);
|
|
562
|
-
if (!Array.isArray(identifiers)) identifiers = [identifiers];
|
|
563
|
-
this.peerJoined = this.peerJoined.bind(this);
|
|
564
|
-
this.peerLeft = this.peerLeft.bind(this);
|
|
565
|
-
this.starLeft = this.starLeft.bind(this);
|
|
566
|
-
this.starJoined = this.starJoined.bind(this);
|
|
567
|
-
|
|
568
|
-
this._init(identifiers, stars);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async _init(identifiers, stars = []) {
|
|
572
|
-
if (stars.length === 0) {
|
|
573
|
-
stars.push('wss://star.leofcoin.org');
|
|
574
|
-
}
|
|
575
|
-
this.identifiers = identifiers;
|
|
576
|
-
this.starsConfig = stars;
|
|
577
|
-
// reconnectJob()
|
|
578
|
-
|
|
579
|
-
globalThis.wrtc = await import('wrtc');
|
|
580
|
-
for (const star of stars) {
|
|
581
|
-
try {
|
|
582
|
-
this.socketClient = await socketRequestClient(star, identifiers[0]);
|
|
583
|
-
const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
|
|
584
|
-
this.socketClient.peerId = id;
|
|
585
|
-
this.#stars[id] = this.socketClient;
|
|
586
|
-
} catch (e) {
|
|
587
|
-
if (stars.indexOf(star) === stars.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
const peers = await this.socketClient.peernet.join({id: this.id});
|
|
591
|
-
for (const id of peers) {
|
|
592
|
-
if (id !== this.id && !this.#connections[id]) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id, peerId: id});
|
|
593
|
-
}
|
|
594
|
-
this.setupListeners();
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
setupListeners() {
|
|
598
|
-
this.socketClient.subscribe('peer:joined', this.peerJoined);
|
|
599
|
-
this.socketClient.subscribe('peer:left', this.peerLeft);
|
|
600
|
-
this.socketClient.subscribe('star:left', this.starLeft);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
starJoined(id) {
|
|
604
|
-
if (this.#stars[id]) {
|
|
605
|
-
this.#stars[id].close();
|
|
606
|
-
delete this.#stars[id];
|
|
607
|
-
}
|
|
608
|
-
console.log(`star ${id} joined`);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
async starLeft(id) {
|
|
612
|
-
if (this.#stars[id]) {
|
|
613
|
-
this.#stars[id].close();
|
|
614
|
-
delete this.#stars[id];
|
|
615
|
-
}
|
|
616
|
-
if (this.socketClient?.peerId === id) {
|
|
617
|
-
|
|
618
|
-
this.socketClient.unsubscribe('peer:joined', this.peerJoined);
|
|
619
|
-
this.socketClient.unsubscribe('peer:left', this.peerLeft);
|
|
620
|
-
this.socketClient.unsubscribe('star:left', this.starLeft);
|
|
621
|
-
this.socketClient.close();
|
|
622
|
-
this.socketClient = undefined;
|
|
623
|
-
|
|
624
|
-
for (const star of this.starsConfig) {
|
|
625
|
-
try {
|
|
626
|
-
this.socketClient = await socketRequestClient(star, this.identifiers[0]);
|
|
627
|
-
if (!this.socketClient?.client?._connection.connected) return
|
|
628
|
-
const id = await this.socketClient.request({url: 'id', params: {from: this.id}});
|
|
629
|
-
this.#stars[id] = this.socketClient;
|
|
630
|
-
|
|
631
|
-
this.socketClient.peerId = id;
|
|
632
|
-
|
|
633
|
-
const peers = await this.socketClient.peernet.join({id: this.id});
|
|
634
|
-
this.setupListeners();
|
|
635
|
-
for (const id of peers) {
|
|
636
|
-
if (id !== this.id) {
|
|
637
|
-
// close connection
|
|
638
|
-
if (this.#connections[id]) {
|
|
639
|
-
if (this.#connections[id].connected) await this.#connections[id].close();
|
|
640
|
-
delete this.#connections[id];
|
|
641
|
-
}
|
|
642
|
-
// reconnect
|
|
643
|
-
if (id !== this.id) this.#connections[id] = new Peer({channelName: `${id}:${this.id}`, socketClient: this.socketClient, id: this.id, to: id, peerId: id});
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
} catch (e) {
|
|
648
|
-
console.log(e);
|
|
649
|
-
if (this.starsConfig.indexOf(star) === this.starsConfig.length -1 && !this.socketClient) throw new Error(`No star available to connect`);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
debug(`star ${id} left`);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
peerLeft(peer) {
|
|
657
|
-
const id = peer.peerId || peer;
|
|
658
|
-
if (this.#connections[id]) {
|
|
659
|
-
this.#connections[id].close();
|
|
660
|
-
delete this.#connections[id];
|
|
661
|
-
}
|
|
662
|
-
debug(`peer ${id} left`);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
peerJoined(peer, signal) {
|
|
666
|
-
const id = peer.peerId || peer;
|
|
667
|
-
if (this.#connections[id]) {
|
|
668
|
-
if (this.#connections[id].connected) this.#connections[id].close();
|
|
669
|
-
delete this.#connections[id];
|
|
670
|
-
}
|
|
671
|
-
// RTCPeerConnection
|
|
672
|
-
this.#connections[id] = new Peer({initiator: true, channelName: `${this.id}:${id}`, socketClient: this.socketClient, id: this.id, to: id, peerId: id});
|
|
673
|
-
debug(`peer ${id} joined`);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
removePeer(peer) {
|
|
677
|
-
const id = peer.peerId || peer;
|
|
678
|
-
if (this.#connections[id]) {
|
|
679
|
-
this.#connections[id].connected && this.#connections[id].close();
|
|
680
|
-
delete this.#connections[id];
|
|
681
|
-
}
|
|
682
|
-
debug(`peer ${id} removed`);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
var proto$a = `
|
|
689
|
-
// PeernetMessage
|
|
690
|
-
message PeernetMessage {
|
|
691
|
-
required bytes data = 1;
|
|
692
|
-
required bytes signature = 2;
|
|
693
|
-
optional string from = 3;
|
|
694
|
-
optional string to = 4;
|
|
695
|
-
optional string id = 5;
|
|
696
|
-
}`;
|
|
697
|
-
|
|
698
|
-
class PeernetMessage extends FormatInterface {
|
|
699
|
-
get keys() {
|
|
700
|
-
return ['data', 'signature', 'from', 'to', 'id']
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
constructor(buffer) {
|
|
704
|
-
const name = 'peernet-message';
|
|
705
|
-
super(buffer, protons(proto$a).PeernetMessage, {name});
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
var proto$9 = `
|
|
710
|
-
// PeernetDHTMessage
|
|
711
|
-
message PeernetDHTMessage {
|
|
712
|
-
required string hash = 1;
|
|
713
|
-
optional string store = 2;
|
|
714
|
-
}
|
|
715
|
-
`;
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* @example `
|
|
719
|
-
new DHTMessage(hash, store)
|
|
720
|
-
// store = optional if not set, peernet checks every store
|
|
721
|
-
let message = new DHTMessage('hashmvbs124xcfd...', 'transaction')
|
|
722
|
-
message = new DHTMessage('hashmvbs124xcfd...', 'block')
|
|
723
|
-
`
|
|
724
|
-
*/
|
|
725
|
-
class DHTMessage extends FormatInterface {
|
|
726
|
-
/**
|
|
727
|
-
*
|
|
728
|
-
*/
|
|
729
|
-
get keys() {
|
|
730
|
-
return ['hash', 'store']
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
constructor(data) {
|
|
734
|
-
const name = 'peernet-dht';
|
|
735
|
-
super(data, protons(proto$9).PeernetDHTMessage, {name});
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
var proto$8 = `
|
|
740
|
-
// PeernetDHTMessageResponse
|
|
741
|
-
message PeernetDHTMessageResponse {
|
|
742
|
-
required string hash = 1;
|
|
743
|
-
required bool has = 2;
|
|
744
|
-
}
|
|
745
|
-
`;
|
|
746
|
-
|
|
747
|
-
class DHTMessageResponse extends FormatInterface {
|
|
748
|
-
get keys() {
|
|
749
|
-
return ['hash', 'has']
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
constructor(data) {
|
|
753
|
-
const name = 'peernet-dht-response';
|
|
754
|
-
super(data, protons(proto$8).PeernetDHTMessageResponse, {name});
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
var proto$7 = `
|
|
759
|
-
// PeernetDataMessage
|
|
760
|
-
message PeernetDataMessage {
|
|
761
|
-
required string hash = 1;
|
|
762
|
-
optional string store = 2;
|
|
763
|
-
}
|
|
764
|
-
`;
|
|
765
|
-
|
|
766
|
-
/**
|
|
767
|
-
* @extends {CodecFormat}
|
|
768
|
-
*/
|
|
769
|
-
class DataMessage extends FormatInterface {
|
|
770
|
-
get keys() {
|
|
771
|
-
return ['hash', 'store']
|
|
772
|
-
}
|
|
773
|
-
/**
|
|
774
|
-
* @param {Buffer|String|Object|DataMessage} data - The data needed to create the DataMessage
|
|
775
|
-
*/
|
|
776
|
-
constructor(data) {
|
|
777
|
-
super(data, protons(proto$7).PeernetDataMessage, {name: 'peernet-data'});
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
var proto$6 = `
|
|
782
|
-
// PsMessage
|
|
783
|
-
message PsMessage {
|
|
784
|
-
required bytes data = 1;
|
|
785
|
-
required bytes topic = 2;
|
|
786
|
-
}`;
|
|
787
|
-
|
|
788
|
-
class PsMessage extends FormatInterface {
|
|
789
|
-
get keys() {
|
|
790
|
-
return ['data', 'topic']
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
constructor(buffer) {
|
|
794
|
-
const name = 'peernet-ps';
|
|
795
|
-
super(buffer, protons(proto$6).PsMessage, {name});
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
var proto$5 = `
|
|
800
|
-
// PeernetPeerMessage
|
|
801
|
-
message PeernetPeerMessage {
|
|
802
|
-
required string id = 1;
|
|
803
|
-
}
|
|
804
|
-
`;
|
|
805
|
-
|
|
806
|
-
class PeerMessage extends FormatInterface {
|
|
807
|
-
get keys() {
|
|
808
|
-
return ['id']
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
constructor(data) {
|
|
812
|
-
const name = 'peernet-peer';
|
|
813
|
-
super(data, protons(proto$5).PeernetPeerMessage, {name});
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
var proto$4 = `
|
|
818
|
-
// PeernetRequestMessage
|
|
819
|
-
message PeernetRequestMessage {
|
|
820
|
-
required string request = 1;
|
|
821
|
-
}
|
|
822
|
-
`;
|
|
823
|
-
|
|
824
|
-
class RequestMessage extends FormatInterface {
|
|
825
|
-
get keys() {
|
|
826
|
-
return ['request']
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
constructor(data) {
|
|
830
|
-
const name = 'peernet-request';
|
|
831
|
-
super(data, protons(proto$4).PeernetRequestMessage, {name});
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
var proto$3 = `
|
|
836
|
-
// PeernetResponseMessage
|
|
837
|
-
message PeernetResponseMessage {
|
|
838
|
-
required bytes response = 1;
|
|
839
|
-
}
|
|
840
|
-
`;
|
|
841
|
-
|
|
842
|
-
class ResponseMessage extends FormatInterface {
|
|
843
|
-
get keys() {
|
|
844
|
-
return ['response']
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
constructor(data) {
|
|
848
|
-
const name = 'peernet-response';
|
|
849
|
-
super(data, protons(proto$3).PeernetResponseMessage, {name});
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
var proto$2 = `
|
|
854
|
-
// PeernetPeerMessageResponse
|
|
855
|
-
message PeernetPeerMessageResponse {
|
|
856
|
-
required string id = 1;
|
|
857
|
-
}
|
|
858
|
-
`;
|
|
859
|
-
|
|
860
|
-
class PeerMessageResponse extends FormatInterface {
|
|
861
|
-
get keys() {
|
|
862
|
-
return ['id']
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
constructor(data) {
|
|
866
|
-
const name = 'peernet-peer-response';
|
|
867
|
-
super(data, protons(proto$2).PeernetPeerMessageResponse, {name});
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
var proto$1 = `
|
|
872
|
-
// PeernetDataMessageResponse
|
|
873
|
-
message PeernetDataMessageResponse {
|
|
874
|
-
required string hash = 1;
|
|
875
|
-
required bytes data = 2;
|
|
876
|
-
}
|
|
877
|
-
`;
|
|
878
|
-
|
|
879
|
-
class DataMessageResponse extends FormatInterface {
|
|
880
|
-
get keys() {
|
|
881
|
-
return ['hash', 'data']
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
constructor(data) {
|
|
885
|
-
const name = 'peernet-data-response';
|
|
886
|
-
super(data, protons(proto$1).PeernetDataMessageResponse, {name});
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
var proto = `
|
|
891
|
-
message ChatMessage {
|
|
892
|
-
required string value = 1;
|
|
893
|
-
required string author = 2;
|
|
894
|
-
required uint64 timestamp = 3;
|
|
895
|
-
repeated string files = 4;
|
|
896
|
-
}`;
|
|
897
|
-
|
|
898
|
-
class ChatMessage extends FormatInterface {
|
|
899
|
-
get keys() {
|
|
900
|
-
return ['author', 'value', 'timestamp', 'files']
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
constructor(buffer) {
|
|
904
|
-
const name = 'chat-message';
|
|
905
|
-
super(buffer, protons(proto).ChatMessage, {name});
|
|
906
|
-
}
|
|
907
|
-
}
|
|
2
|
+
import { Codec, CodecHash, codecs } from '@leofcoin/codec-format-interface';
|
|
3
|
+
import MultiWallet from '@leofcoin/multi-wallet';
|
|
908
4
|
|
|
909
5
|
const protoFor = (data) => {
|
|
910
6
|
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
@@ -981,12 +77,12 @@ class PeerDiscovery {
|
|
|
981
77
|
async discover(peer) {
|
|
982
78
|
let id = this._getPeerId(peer.id);
|
|
983
79
|
if (id) return id
|
|
984
|
-
const data = new peernet.protos['peernet-peer']({id: this.id});
|
|
80
|
+
const data = await new peernet.protos['peernet-peer']({id: this.id});
|
|
985
81
|
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
986
82
|
|
|
987
83
|
let response = await peer.request(node.encoded);
|
|
988
|
-
response = protoFor(response);
|
|
989
|
-
response = new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
84
|
+
response = await protoFor(response);
|
|
85
|
+
response = await new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
990
86
|
|
|
991
87
|
id = response.decoded.id;
|
|
992
88
|
if (id === this.id) return;
|
|
@@ -1017,7 +113,7 @@ class PeerDiscovery {
|
|
|
1017
113
|
peernet.peerMap.set(from, connections);
|
|
1018
114
|
}
|
|
1019
115
|
}
|
|
1020
|
-
const data = new peernet.protos['peernet-peer-response']({id: this.id});
|
|
116
|
+
const data = await new peernet.protos['peernet-peer-response']({id: this.id});
|
|
1021
117
|
const node = await peernet.prepareMessage(from, data.encoded);
|
|
1022
118
|
|
|
1023
119
|
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
@@ -1157,527 +253,6 @@ class DhtEarth {
|
|
|
1157
253
|
}
|
|
1158
254
|
}
|
|
1159
255
|
|
|
1160
|
-
/**
|
|
1161
|
-
* @params {String} network
|
|
1162
|
-
* @return {object} { identity, accounts, config }
|
|
1163
|
-
*/
|
|
1164
|
-
var generateAccount = async network => {
|
|
1165
|
-
let wallet = new MultiWallet$1(network);
|
|
1166
|
-
/**
|
|
1167
|
-
* @type {string}
|
|
1168
|
-
*/
|
|
1169
|
-
const mnemonic = await wallet.generate();
|
|
1170
|
-
|
|
1171
|
-
wallet = new MultiWallet$1(network);
|
|
1172
|
-
await wallet.recover(mnemonic, network);
|
|
1173
|
-
/**
|
|
1174
|
-
* @type {object}
|
|
1175
|
-
*/
|
|
1176
|
-
const account = wallet.account(0);
|
|
1177
|
-
/**
|
|
1178
|
-
* @type {object}
|
|
1179
|
-
*/
|
|
1180
|
-
const external = account.external(0);
|
|
1181
|
-
const internal = account.internal(0);
|
|
1182
|
-
|
|
1183
|
-
return {
|
|
1184
|
-
identity: {
|
|
1185
|
-
mnemonic,
|
|
1186
|
-
// multiWIF: wallet.export(),
|
|
1187
|
-
publicKey: external.publicKey,
|
|
1188
|
-
privateKey: external.privateKey,
|
|
1189
|
-
walletId: external.id
|
|
1190
|
-
},
|
|
1191
|
-
accounts: [['main account', external.address, internal.address]]
|
|
1192
|
-
// config: {
|
|
1193
|
-
// }
|
|
1194
|
-
}
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
var testnets = {
|
|
1198
|
-
'leofcoin:olivia': {
|
|
1199
|
-
messagePrefix: '\u0019Leofcoin Signed Message:',
|
|
1200
|
-
pubKeyHash: 0x73, // o
|
|
1201
|
-
scriptHash: 0x76, // p
|
|
1202
|
-
multiTxHash: 0x8b4125, // omtx
|
|
1203
|
-
payments: {
|
|
1204
|
-
version: 0,
|
|
1205
|
-
unspent: 0x1fa443d7 // ounsp
|
|
1206
|
-
},
|
|
1207
|
-
wif: 0x7D, // s
|
|
1208
|
-
multiCodec: 0x7c4,
|
|
1209
|
-
bip32: { public: 0x13BBF2D5, private: 0x13BBCBC5 }
|
|
1210
|
-
},
|
|
1211
|
-
'bitcoin:testnet': {
|
|
1212
|
-
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
1213
|
-
bech32: 'tb',
|
|
1214
|
-
pubKeyHash: 0x6f,
|
|
1215
|
-
scriptHash: 0xc4,
|
|
1216
|
-
wif: 0xef,
|
|
1217
|
-
bip32: {
|
|
1218
|
-
public: 0x043587cf,
|
|
1219
|
-
private: 0x04358394
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
};
|
|
1224
|
-
|
|
1225
|
-
// https://en.bitcoin.it/wiki/List_of_address_prefixes
|
|
1226
|
-
/**
|
|
1227
|
-
* Main network
|
|
1228
|
-
* @return {messagePrefix, pubKeyHash, scriptHash, wif, bip32}
|
|
1229
|
-
*/
|
|
1230
|
-
const leofcoin = {
|
|
1231
|
-
messagePrefix: '\u0019Leofcoin Signed Message:',
|
|
1232
|
-
pubKeyHash: 0x30, // L
|
|
1233
|
-
scriptHash: 0x37, // P
|
|
1234
|
-
multiTxHash: 0x3adeed, // Lmtx
|
|
1235
|
-
payments: {
|
|
1236
|
-
version: 0,
|
|
1237
|
-
unspent: 0x0d6e0327 // Lunsp
|
|
1238
|
-
},
|
|
1239
|
-
coin_type: 640,
|
|
1240
|
-
wif: 0x3F, // S
|
|
1241
|
-
multiCodec: 0x3c4,
|
|
1242
|
-
bip32: { public: 0x13BBF2D4, private: 0x13BBCBC4 },
|
|
1243
|
-
testnet: testnets['leofcoin:olivia']
|
|
1244
|
-
};
|
|
1245
|
-
|
|
1246
|
-
const bitcoin = {
|
|
1247
|
-
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
1248
|
-
bech32: 'bc',
|
|
1249
|
-
pubKeyHash: 0x00,
|
|
1250
|
-
multiCodec: 0x00,
|
|
1251
|
-
scriptHash: 0x05,
|
|
1252
|
-
wif: 0x80,
|
|
1253
|
-
coin_type: 0,
|
|
1254
|
-
bip32: {
|
|
1255
|
-
public: 0x0488b21e, private: 0x0488ade4
|
|
1256
|
-
},
|
|
1257
|
-
testnet: testnets['bitcoin:testnet']
|
|
1258
|
-
};
|
|
1259
|
-
|
|
1260
|
-
const litecoin = {
|
|
1261
|
-
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
1262
|
-
pubKeyHash: 0x30,
|
|
1263
|
-
scriptHash: 0x32,
|
|
1264
|
-
wif: 0xb0,
|
|
1265
|
-
bip32: {
|
|
1266
|
-
public: 0x019da462,
|
|
1267
|
-
private: 0x019d9cfe
|
|
1268
|
-
}
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
const ethereum = {
|
|
1272
|
-
messagePrefix: '\x19Ethereum Signed Message:\n',
|
|
1273
|
-
pubKeyHash: 0x30,
|
|
1274
|
-
scriptHash: 0x32,
|
|
1275
|
-
bip32: {
|
|
1276
|
-
private: 0x0488ADE4, public: 0x0488B21E
|
|
1277
|
-
},
|
|
1278
|
-
coin_type: 60,
|
|
1279
|
-
wif: 0x45,//E
|
|
1280
|
-
multiCodec: 0x3c5
|
|
1281
|
-
};
|
|
1282
|
-
|
|
1283
|
-
/**
|
|
1284
|
-
* Our & supported networks
|
|
1285
|
-
* @return {leofcoin, olivia}
|
|
1286
|
-
*/
|
|
1287
|
-
var networks = {
|
|
1288
|
-
leofcoin,
|
|
1289
|
-
bitcoin,
|
|
1290
|
-
litecoin,
|
|
1291
|
-
ethereum
|
|
1292
|
-
};
|
|
1293
|
-
|
|
1294
|
-
const fromNetworkString = network => {
|
|
1295
|
-
const parts = network.split(':');
|
|
1296
|
-
network = networks[parts[0]];
|
|
1297
|
-
if (parts[1]) {
|
|
1298
|
-
if (network[parts[1]]) network = network[parts[1]];
|
|
1299
|
-
|
|
1300
|
-
network.coin_type = 1;
|
|
1301
|
-
}
|
|
1302
|
-
return network;
|
|
1303
|
-
};
|
|
1304
|
-
|
|
1305
|
-
// import { createHash } from 'crypto'
|
|
1306
|
-
// import { createHash as _createHash } from './hash'
|
|
1307
|
-
|
|
1308
|
-
const { encode: encode$1, decode: decode$1 } = bs58check__default;
|
|
1309
|
-
class HDWallet {
|
|
1310
|
-
|
|
1311
|
-
get chainCodeBuffer() {
|
|
1312
|
-
return this.ifNotLocked(() => this.hdnode.chainCode)
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
get chainCode() {
|
|
1316
|
-
return this.ifNotLocked(() => this.chainCodeBuffer.toString('hex'))
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
get privateKeyBuffer() {
|
|
1320
|
-
return this.ifNotLocked(() => this.hdnode.privateKey)
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
get privateKey() {
|
|
1324
|
-
return this.ifNotLocked(() => this.privateKeyBuffer.toString('hex'))
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
get publicKeyBuffer() {
|
|
1328
|
-
return this.ifNotLocked(() => this.hdnode.publicKey)
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
get publicKey() {
|
|
1332
|
-
return this.ifNotLocked(() => this.publicKeyBuffer.toString('hex'))
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
get ethereumAddress() {
|
|
1336
|
-
const buffer = ecc.pointFromScalar(this.hdnode.__D, false);
|
|
1337
|
-
let hash = createKeccakHash('keccak256').update(buffer.slice(1)).digest();
|
|
1338
|
-
return `0x${hash.slice(-20).toString('hex')}`
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
// async bitcoinAddress() {
|
|
1342
|
-
// const chainCode = this.privateKeyBuffer
|
|
1343
|
-
//
|
|
1344
|
-
// const node = bip32.fromPrivateKey(this.privateKeyBuffer, chainCode, networks['bitcoin'])
|
|
1345
|
-
// let buffer = await _createHash(node.publicKey, 'SHA-256')
|
|
1346
|
-
// buffer = createHash('ripemd160').update(buffer).digest()
|
|
1347
|
-
// // buffer = Buffer.from(`0x00${buffer.toString('hex')}`, 'hex')
|
|
1348
|
-
// // buffer = createHash('sha256').update(buffer).digest()
|
|
1349
|
-
// // const mainHash = buffer
|
|
1350
|
-
// // buffer = createHash('sha256').update(buffer).digest()
|
|
1351
|
-
// // const checksum = buffer.toString('hex').substring(0, 8)
|
|
1352
|
-
// // return base58.encode(Buffer.concat([mainHash, Buffer.from(checksum, 'hex')]))
|
|
1353
|
-
// const payload = Buffer.allocUnsafe(21)
|
|
1354
|
-
// payload.writeUInt8(networks['bitcoin'].pubKeyHash, 0)
|
|
1355
|
-
// buffer.copy(payload, 1)
|
|
1356
|
-
//
|
|
1357
|
-
// return encode(payload)
|
|
1358
|
-
// }
|
|
1359
|
-
|
|
1360
|
-
get leofcoinAddress() {
|
|
1361
|
-
return encode$1(this.neutered.publicKeyBuffer)
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
get address() {
|
|
1365
|
-
return this.getAddressForCoin()
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
getAddressForCoin(coin_type) {
|
|
1369
|
-
if (!coin_type) coin_type = this.hdnode.network.coin_type;
|
|
1370
|
-
if (coin_type === 1) {
|
|
1371
|
-
if (this.networkName?.split(':')[0] === 'ethereum') coin_type = 60;
|
|
1372
|
-
if (this.networkName?.split(':')[0] === 'leofcoin') coin_type = 640;
|
|
1373
|
-
}
|
|
1374
|
-
// if (coin_type === 0) return this.bitcoinAddress
|
|
1375
|
-
if (coin_type === 60) return this.ethereumAddress
|
|
1376
|
-
if (coin_type === 640) return this.leofcoinAddress
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
get accountAddress() {
|
|
1380
|
-
return this.ifNotLocked(() => encode$1(this.hdnode.publicKeyBuffer))
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
get isTestnet() {
|
|
1384
|
-
if (typeof network === 'string')
|
|
1385
|
-
this.hdnode.network = fromNetworkString(network);
|
|
1386
|
-
|
|
1387
|
-
return Boolean(this.hdnode.network.coin_type === 1)
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
constructor(network, hdnode) {
|
|
1391
|
-
if (typeof network === 'string') {
|
|
1392
|
-
this.networkName = network;
|
|
1393
|
-
this.network = fromNetworkString(network);
|
|
1394
|
-
} else if (typeof network === 'object')
|
|
1395
|
-
this.network = network;
|
|
1396
|
-
|
|
1397
|
-
if (hdnode) this.defineHDNode(hdnode);
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
ifNotLocked(fn, params) {
|
|
1401
|
-
if (!this.locked) return fn(params);
|
|
1402
|
-
return null
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
defineHDNode(value) {
|
|
1406
|
-
Object.defineProperty(this, 'hdnode', {
|
|
1407
|
-
configurable: false,
|
|
1408
|
-
writable: false,
|
|
1409
|
-
value: value
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
validateNetwork(network) {
|
|
1414
|
-
if (!network && !this.network) return console.error(`expected network to be defined`);
|
|
1415
|
-
if (!network && this.network) network = this.network;
|
|
1416
|
-
if (typeof network === 'string') network = fromNetworkString(network);
|
|
1417
|
-
if (typeof network !== 'object') return console.error('network not found');
|
|
1418
|
-
return network;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
async generate(password, network) {
|
|
1422
|
-
network = this.validateNetwork(network);
|
|
1423
|
-
const mnemonic = new Mnemonic().generate();
|
|
1424
|
-
const seed = new Mnemonic().seedFromMnemonic(mnemonic, password);
|
|
1425
|
-
this.defineHDNode(bip32.fromSeed(seed, network));
|
|
1426
|
-
return mnemonic;
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
/**
|
|
1430
|
-
* recover using mnemonic (recovery word list)
|
|
1431
|
-
*/
|
|
1432
|
-
async recover(mnemonic, password, network) {
|
|
1433
|
-
network = this.validateNetwork(network, password);
|
|
1434
|
-
const seed = new Mnemonic().seedFromMnemonic(mnemonic, password);
|
|
1435
|
-
this.defineHDNode(bip32.fromSeed(seed, network));
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
load(base58, network) {
|
|
1439
|
-
network = this.validateNetwork(network);
|
|
1440
|
-
this.defineHDNode(bip32.fromBase58(base58, network));
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
save() {
|
|
1444
|
-
return this.hdnode.toBase58();
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
fromAddress(address, chainCode, network) {
|
|
1448
|
-
network = this.validateNetwork(network);
|
|
1449
|
-
// if (network.coin_type === 60) {
|
|
1450
|
-
// address = Buffer.from(address, 'hex')
|
|
1451
|
-
// } else {
|
|
1452
|
-
address = decode$1(address);
|
|
1453
|
-
// }
|
|
1454
|
-
|
|
1455
|
-
if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = address.slice(1);
|
|
1456
|
-
this.defineHDNode(bip32.fromPublicKey(address, chainCode, network));
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
fromPublicKey(hex, chainCode, network) {
|
|
1460
|
-
network = this.validateNetwork(network);
|
|
1461
|
-
if (!Buffer.isBuffer(hex)) hex = Buffer.from(hex, 'hex');
|
|
1462
|
-
if (!chainCode || chainCode && !Buffer.isBuffer(chainCode)) chainCode = hex.slice(1);
|
|
1463
|
-
this.defineHDNode(bip32.fromPublicKey(hex, chainCode, network));
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
const { subtle } = crypto;
|
|
1468
|
-
|
|
1469
|
-
const generateAesKey = async (length = 256) => {
|
|
1470
|
-
const key = await subtle.generateKey({
|
|
1471
|
-
name: 'AES-CBC',
|
|
1472
|
-
length
|
|
1473
|
-
}, true, ['encrypt', 'decrypt']);
|
|
1474
|
-
|
|
1475
|
-
return key;
|
|
1476
|
-
};
|
|
1477
|
-
|
|
1478
|
-
const importAesKey = async (exported, format = 'raw', length = 256) => {
|
|
1479
|
-
return await subtle.importKey(format, exported, {
|
|
1480
|
-
name: 'AES-CBC',
|
|
1481
|
-
length
|
|
1482
|
-
}, true, ['encrypt', 'decrypt'])
|
|
1483
|
-
};
|
|
1484
|
-
|
|
1485
|
-
const exportAesKey = async (key, format = 'raw') => {
|
|
1486
|
-
return await subtle.exportKey(format, key)
|
|
1487
|
-
};
|
|
1488
|
-
|
|
1489
|
-
const encryptAes = async (uint8Array, key, iv) => subtle.encrypt({
|
|
1490
|
-
name: 'AES-CBC',
|
|
1491
|
-
iv,
|
|
1492
|
-
}, key, uint8Array);
|
|
1493
|
-
|
|
1494
|
-
const uint8ArrayToHex = uint8Array =>
|
|
1495
|
-
[...uint8Array].map(x => x.toString(16).padStart(2, '0')).join('');
|
|
1496
|
-
|
|
1497
|
-
const arrayBufferToHex = arrayBuffer =>
|
|
1498
|
-
uint8ArrayToHex(new Uint8Array(arrayBuffer));
|
|
1499
|
-
|
|
1500
|
-
const hexToUint8Array = hex =>
|
|
1501
|
-
new Uint8Array(hex.match(/[\da-f]{2}/gi).map(x => parseInt(x, 16)));
|
|
1502
|
-
|
|
1503
|
-
const encrypt = async string => {
|
|
1504
|
-
const ec = new TextEncoder();
|
|
1505
|
-
const key = await generateAesKey();
|
|
1506
|
-
const iv = await randombytes(16);
|
|
1507
|
-
|
|
1508
|
-
const ciphertext = await encryptAes(ec.encode(string), key, iv);
|
|
1509
|
-
const exported = await exportAesKey(key);
|
|
1510
|
-
|
|
1511
|
-
return {
|
|
1512
|
-
key: arrayBufferToHex(exported),
|
|
1513
|
-
iv: iv.toString('hex'),
|
|
1514
|
-
cipher: arrayBufferToHex(ciphertext)
|
|
1515
|
-
}
|
|
1516
|
-
};
|
|
1517
|
-
|
|
1518
|
-
const decrypt = async (cipher, key, iv) => {
|
|
1519
|
-
if (!key.type) key = await importAesKey(hexToUint8Array(key));
|
|
1520
|
-
cipher = new Uint8Array(hexToUint8Array(cipher));
|
|
1521
|
-
iv = new Uint8Array(hexToUint8Array(iv));
|
|
1522
|
-
|
|
1523
|
-
const dec = new TextDecoder();
|
|
1524
|
-
const plaintext = await subtle.decrypt({
|
|
1525
|
-
name: 'AES-CBC',
|
|
1526
|
-
iv,
|
|
1527
|
-
}, key, cipher);
|
|
1528
|
-
|
|
1529
|
-
return dec.decode(plaintext);
|
|
1530
|
-
};
|
|
1531
|
-
|
|
1532
|
-
const { encode, decode } = bs58check;
|
|
1533
|
-
|
|
1534
|
-
// TODO: multihash addresses
|
|
1535
|
-
class HDAccount {
|
|
1536
|
-
/**
|
|
1537
|
-
* @param {number} depth - acount depth
|
|
1538
|
-
*/
|
|
1539
|
-
constructor(node, depth = 0) {
|
|
1540
|
-
this.node = node;
|
|
1541
|
-
this.depth = depth;
|
|
1542
|
-
this._prefix = `m/44'/${node.network.coin_type}'/${depth}'/`;
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
/**
|
|
1546
|
-
* @param {number} index - address index
|
|
1547
|
-
*/
|
|
1548
|
-
internal(index = 0) {
|
|
1549
|
-
return this.node.derivePath(`${this._prefix}1/${index}`)
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
/**
|
|
1553
|
-
* @param {number} index - address index
|
|
1554
|
-
*/
|
|
1555
|
-
external(index = 0) {
|
|
1556
|
-
return this.node.derivePath(`${this._prefix}0/${index}`)
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
class MultiWallet extends HDWallet {
|
|
1561
|
-
constructor(network, hdnode) {
|
|
1562
|
-
super(network, hdnode);
|
|
1563
|
-
this.multiCodec = this.network.multiCodec;
|
|
1564
|
-
this.version = 0x00;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
get id() {
|
|
1568
|
-
const buffer = Buffer.concat([
|
|
1569
|
-
Buffer.from(varint.encode(this.multiCodec)),
|
|
1570
|
-
Buffer.from(this.account(0).node.neutered.publicKey, 'hex')
|
|
1571
|
-
]);
|
|
1572
|
-
return encode(buffer)
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
get multiWIF() {
|
|
1576
|
-
return this.ifNotLocked(() => this.encode())
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
get neutered() {
|
|
1580
|
-
const neutered = this.ifNotLocked(() => new MultiWallet(this.networkName, this.hdnode.neutered()));
|
|
1581
|
-
if (neutered) this._neutered = neutered;
|
|
1582
|
-
return this._neutered
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
fromId(id) {
|
|
1586
|
-
let buffer = decode(id);
|
|
1587
|
-
varint.decode(buffer);
|
|
1588
|
-
buffer = buffer.slice(varint.decode.bytes);
|
|
1589
|
-
this.fromPublicKey(buffer, null, this.networkName);
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
async lock(multiWIF) {
|
|
1593
|
-
if (!multiWIF) multiWIF = this.multiWIF;
|
|
1594
|
-
this.encrypted = await encrypt(multiWIF.toString('hex'));
|
|
1595
|
-
this.locked = true;
|
|
1596
|
-
return this.encrypted
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
async unlock({key, iv, cipher}) {
|
|
1600
|
-
const decrypted = await decrypt(cipher, key, iv);
|
|
1601
|
-
this.import(decrypted);
|
|
1602
|
-
this.locked = false;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
export() {
|
|
1606
|
-
return this.encode();
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
/**
|
|
1610
|
-
* encodes the multiWIF and loads wallet from bs58
|
|
1611
|
-
*
|
|
1612
|
-
* @param {multiWIF} multiWIF - note a multiWIF is not the same as a wif
|
|
1613
|
-
*/
|
|
1614
|
-
import(multiWIF) {
|
|
1615
|
-
const { bs58, version, multiCodec } = this.decode(multiWIF);
|
|
1616
|
-
this.network = Object.values(networks).reduce((p, c) => {
|
|
1617
|
-
if (c.multiCodec===multiCodec) return c
|
|
1618
|
-
else if (c.testnet && c.testnet.multiCodec === multiCodec) return c.testnet
|
|
1619
|
-
else return p
|
|
1620
|
-
}, networks['leofcoin']);
|
|
1621
|
-
this.load(bs58, this.networkName);
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
/**
|
|
1625
|
-
* @return base58Check encoded string
|
|
1626
|
-
*/
|
|
1627
|
-
encode() {
|
|
1628
|
-
const buffer = Buffer.concat([
|
|
1629
|
-
Buffer.from(varint.encode(this.version)),
|
|
1630
|
-
Buffer.from(varint.encode(this.multiCodec)),
|
|
1631
|
-
decode(this.save())
|
|
1632
|
-
]);
|
|
1633
|
-
return encode(buffer);
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
decode(bs58) {
|
|
1637
|
-
let buffer = decode(bs58);
|
|
1638
|
-
const version = varint.decode(buffer);
|
|
1639
|
-
buffer = buffer.slice(varint.decode.bytes);
|
|
1640
|
-
const multiCodec = varint.decode(buffer);
|
|
1641
|
-
buffer = buffer.slice(varint.decode.bytes);
|
|
1642
|
-
bs58 = encode(buffer);
|
|
1643
|
-
if (version !== this.version) throw TypeError('Invalid version');
|
|
1644
|
-
if (this.multiCodec !== multiCodec) throw TypeError('Invalid multiCodec');
|
|
1645
|
-
return { version, multiCodec, bs58 };
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
sign(hash) {
|
|
1649
|
-
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1650
|
-
.sign(hash, this.privateKeyBuffer);
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
verify(multiSignature, hash) {
|
|
1655
|
-
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1656
|
-
.verify(multiSignature, hash, this.publicKeyBuffer)
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
/**
|
|
1660
|
-
* @param {number} account - account to return chain for
|
|
1661
|
-
* @return { internal(addressIndex), external(addressIndex) }
|
|
1662
|
-
*/
|
|
1663
|
-
account(index) {
|
|
1664
|
-
return new HDAccount(new MultiWallet(this.networkName, this.hdnode), index);
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
/**
|
|
1668
|
-
* m / purpose' / coin_type' / account' / change / aadress_index
|
|
1669
|
-
*
|
|
1670
|
-
* see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
|
1671
|
-
*/
|
|
1672
|
-
derivePath(path) {
|
|
1673
|
-
return new MultiWallet(this.networkName, this.hdnode.derivePath(path))
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
derive(index) {
|
|
1677
|
-
return new MultiWallet(this.networkName, this.hdnode.derive(index));
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
256
|
class MessageHandler {
|
|
1682
257
|
constructor(network) {
|
|
1683
258
|
this.network = network;
|
|
@@ -1716,7 +291,7 @@ class MessageHandler {
|
|
|
1716
291
|
data,
|
|
1717
292
|
};
|
|
1718
293
|
const signature = await this.hashAndSignMessage(message);
|
|
1719
|
-
const node = new
|
|
294
|
+
const node = await new globalThis.peernet.protos['peernet-message']({
|
|
1720
295
|
...message,
|
|
1721
296
|
signature,
|
|
1722
297
|
});
|
|
@@ -1730,8 +305,8 @@ const dataHandler = async message => {
|
|
|
1730
305
|
|
|
1731
306
|
const {data, id} = message;
|
|
1732
307
|
|
|
1733
|
-
message = protoFor(data);
|
|
1734
|
-
const proto = protoFor(message.decoded.data);
|
|
308
|
+
message = await protoFor(data);
|
|
309
|
+
const proto = await protoFor(message.decoded.data);
|
|
1735
310
|
const from = message.decoded.from;
|
|
1736
311
|
|
|
1737
312
|
peernet._protoHandler({id, proto}, peernet.client.connections[from], from);
|
|
@@ -1797,6 +372,14 @@ class Peernet {
|
|
|
1797
372
|
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
1798
373
|
}
|
|
1799
374
|
|
|
375
|
+
get protos() {
|
|
376
|
+
return globalThis.peernet.protos
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get codecs() {
|
|
380
|
+
return codecs
|
|
381
|
+
}
|
|
382
|
+
|
|
1800
383
|
addProto(name, proto) {
|
|
1801
384
|
if (!this.protos[name]) this.protos[name] = proto;
|
|
1802
385
|
}
|
|
@@ -1806,6 +389,10 @@ class Peernet {
|
|
|
1806
389
|
}
|
|
1807
390
|
|
|
1808
391
|
async addStore(name, prefix, root, isPrivate = true) {
|
|
392
|
+
if (!globalThis.LeofcoinStorage) {
|
|
393
|
+
const importee = await import(/* webpackChunkName: "storage" */ '@leofcoin/storage');
|
|
394
|
+
globalThis.LeofcoinStorage = importee.default;
|
|
395
|
+
}
|
|
1809
396
|
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
1810
397
|
name === 'data' || name === 'message') isPrivate = false;
|
|
1811
398
|
|
|
@@ -1813,7 +400,7 @@ class Peernet {
|
|
|
1813
400
|
if (this.hasDaemon) {
|
|
1814
401
|
Storage = LeofcoinStorageClient;
|
|
1815
402
|
} else {
|
|
1816
|
-
Storage =
|
|
403
|
+
Storage = LeofcoinStorage;
|
|
1817
404
|
}
|
|
1818
405
|
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
1819
406
|
await new Storage(name, root);
|
|
@@ -1878,6 +465,20 @@ class Peernet {
|
|
|
1878
465
|
this.storePrefix = options.storePrefix;
|
|
1879
466
|
this.root = options.root;
|
|
1880
467
|
|
|
468
|
+
const {
|
|
469
|
+
RequestMessage,
|
|
470
|
+
ResponseMessage,
|
|
471
|
+
PeerMessage,
|
|
472
|
+
PeerMessageResponse,
|
|
473
|
+
PeernetMessage,
|
|
474
|
+
DHTMessage,
|
|
475
|
+
DHTMessageResponse,
|
|
476
|
+
DataMessage,
|
|
477
|
+
DataMessageResponse,
|
|
478
|
+
PsMessage,
|
|
479
|
+
ChatMessage
|
|
480
|
+
} = await import(/* webpackChunkName: "messages" */ './messages-421f88db.js');
|
|
481
|
+
|
|
1881
482
|
/**
|
|
1882
483
|
* proto Object containing protos
|
|
1883
484
|
* @type {Object}
|
|
@@ -1887,6 +488,7 @@ class Peernet {
|
|
|
1887
488
|
* @property {DataMessage} protos[peernet-data] messageNode
|
|
1888
489
|
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
1889
490
|
*/
|
|
491
|
+
|
|
1890
492
|
globalThis.peernet.protos = {
|
|
1891
493
|
'peernet-request': RequestMessage,
|
|
1892
494
|
'peernet-response': ResponseMessage,
|
|
@@ -1901,16 +503,11 @@ class Peernet {
|
|
|
1901
503
|
'chat-message': ChatMessage,
|
|
1902
504
|
};
|
|
1903
505
|
|
|
1904
|
-
this.protos = globalThis.peernet.protos;
|
|
1905
|
-
this.codecs = codecs;
|
|
1906
|
-
|
|
1907
506
|
this._messageHandler = new MessageHandler(this.network);
|
|
1908
507
|
|
|
1909
508
|
const {daemon, environment} = await target();
|
|
1910
509
|
this.hasDaemon = daemon;
|
|
1911
510
|
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
511
|
for (const store of this.defaultStores) {
|
|
1915
512
|
await this.addStore(store, options.storePrefix, options.root);
|
|
1916
513
|
}
|
|
@@ -1930,6 +527,9 @@ class Peernet {
|
|
|
1930
527
|
}
|
|
1931
528
|
} catch (e) {
|
|
1932
529
|
if (e.code === 'ERR_NOT_FOUND') {
|
|
530
|
+
|
|
531
|
+
const importee = await import(/* webpackChunkName: "generate-account" */ '@leofcoin/generate-account');
|
|
532
|
+
const generateAccount = importee.default;
|
|
1933
533
|
const {identity, accounts, config} = await generateAccount(this.network);
|
|
1934
534
|
walletStore.put('version', new TextEncoder().encode(1));
|
|
1935
535
|
walletStore.put('accounts', new TextEncoder().encode(JSON.stringify(accounts)));
|
|
@@ -1962,11 +562,13 @@ class Peernet {
|
|
|
1962
562
|
*/
|
|
1963
563
|
pubsub.subscribe('peer:data', dataHandler);
|
|
1964
564
|
|
|
565
|
+
|
|
566
|
+
const importee = await import(/* webpackChunkName: "peernet-swarm" */ '@leofcoin/peernet-swarm');
|
|
1965
567
|
/**
|
|
1966
568
|
* @access public
|
|
1967
569
|
* @type {PeernetClient}
|
|
1968
570
|
*/
|
|
1969
|
-
this.client = new
|
|
571
|
+
this.client = new importee.default(this.id);
|
|
1970
572
|
if (globalThis.onbeforeunload) {
|
|
1971
573
|
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
1972
574
|
}
|
|
@@ -2006,7 +608,7 @@ class Peernet {
|
|
|
2006
608
|
if (store.private) has = false;
|
|
2007
609
|
else has = await store.has(hash);
|
|
2008
610
|
}
|
|
2009
|
-
const data = new
|
|
611
|
+
const data = await new this.protos['peernet-dht-response']({hash, has});
|
|
2010
612
|
const node = await this.prepareMessage(from, data.encoded);
|
|
2011
613
|
|
|
2012
614
|
this.sendMessage(peer, id, node.encoded);
|
|
@@ -2022,7 +624,7 @@ class Peernet {
|
|
|
2022
624
|
data = await store.get(hash);
|
|
2023
625
|
|
|
2024
626
|
if (data) {
|
|
2025
|
-
data = new
|
|
627
|
+
data = await new this.protos['peernet-data-response']({hash, data});
|
|
2026
628
|
|
|
2027
629
|
const node = await this.prepareMessage(from, data.encoded);
|
|
2028
630
|
this.sendMessage(peer, id, node.encoded);
|
|
@@ -2049,16 +651,16 @@ class Peernet {
|
|
|
2049
651
|
*/
|
|
2050
652
|
async walk(hash) {
|
|
2051
653
|
if (!hash) throw new Error('hash expected, received undefined')
|
|
2052
|
-
const data = new
|
|
654
|
+
const data = await new this.protos['peernet-dht']({hash});
|
|
2053
655
|
this.client.id;
|
|
2054
656
|
const walk = async peer => {
|
|
2055
657
|
const node = await this.prepareMessage(peer.peerId, data.encoded);
|
|
2056
658
|
let result = await peer.request(node.encoded);
|
|
2057
659
|
result = new Uint8Array(Object.values(result));
|
|
2058
|
-
let proto = protoFor(result);
|
|
660
|
+
let proto = await protoFor(result);
|
|
2059
661
|
if (proto.name !== 'peernet-message') throw encapsulatedError()
|
|
2060
662
|
const from = proto.decoded.from;
|
|
2061
|
-
proto = protoFor(proto.decoded.data);
|
|
663
|
+
proto = await protoFor(proto.decoded.data);
|
|
2062
664
|
if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
|
|
2063
665
|
|
|
2064
666
|
// TODO: give ip and port (just used for location)
|
|
@@ -2157,7 +759,7 @@ class Peernet {
|
|
|
2157
759
|
if (peer.peerId === id) return peer
|
|
2158
760
|
});
|
|
2159
761
|
|
|
2160
|
-
let data = new
|
|
762
|
+
let data = await new this.protos['peernet-data']({hash, store: store?.name ? store?.name : store});
|
|
2161
763
|
|
|
2162
764
|
const node = await this.prepareMessage(id, data.encoded);
|
|
2163
765
|
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
@@ -2168,8 +770,8 @@ class Peernet {
|
|
|
2168
770
|
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2169
771
|
}
|
|
2170
772
|
data = new Uint8Array(Object.values(data));
|
|
2171
|
-
let proto = protoFor(data);
|
|
2172
|
-
proto = protoFor(proto.decoded.data);
|
|
773
|
+
let proto = await protoFor(data);
|
|
774
|
+
proto = await protoFor(proto.decoded.data);
|
|
2173
775
|
// TODO: store data automaticly or not
|
|
2174
776
|
return proto.decoded.data
|
|
2175
777
|
|
|
@@ -2302,7 +904,7 @@ class Peernet {
|
|
|
2302
904
|
if (topic instanceof Uint8Array === false) topic = new TextEncoder().encode(topic);
|
|
2303
905
|
if (data instanceof Uint8Array === false) data = new TextEncoder().encode(JSON.stringify(data));
|
|
2304
906
|
const id = Math.random().toString(36).slice(-12);
|
|
2305
|
-
data = new
|
|
907
|
+
data = await new this.protos['peernet-ps']({data, topic});
|
|
2306
908
|
for (const peer of this.connections) {
|
|
2307
909
|
if (peer.peerId !== this.peerId) {
|
|
2308
910
|
const node = await this.prepareMessage(peer.peerId, data.encoded);
|
|
@@ -2313,7 +915,7 @@ class Peernet {
|
|
|
2313
915
|
}
|
|
2314
916
|
|
|
2315
917
|
createHash(data, name) {
|
|
2316
|
-
return new
|
|
918
|
+
return new CodeHash(data, {name})
|
|
2317
919
|
}
|
|
2318
920
|
|
|
2319
921
|
/**
|