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