@leofcoin/chain 1.10.19 → 1.10.20
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/exports/browser/{browser-CCfR9KOM-CpStHgTj.js → browser-CCfR9KOM-BGMMnVsR.js} +1 -1
- package/exports/browser/{client-C_pBoi1g-_pfKzCvX.js → client-DKKzPSlb-C_1ug6ux.js} +2 -2
- package/exports/browser/{index-dTQVgkpf-BbdshWB0.js → index-dTQVgkpf-DOL2rIjp.js} +1 -1
- package/exports/browser/{messages-BGHaihSi-eHZVl0y9.js → messages-DCePyqKC-BSvJxDWZ.js} +1 -1
- package/exports/browser/{node-browser-BNe-en1l.js → node-browser-Ha4Lvntj.js} +472 -30
- package/exports/browser/node-browser.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as inflate_1, c as createDebugger, d as deflate_1, L as LittlePubSub } from './node-browser-
|
|
1
|
+
import { i as inflate_1, c as createDebugger, d as deflate_1, L as LittlePubSub } from './node-browser-Ha4Lvntj.js';
|
|
2
2
|
import './browser-crypto-CH0ua22L.js';
|
|
3
3
|
import './connection-monitor-D-krNiRH.js';
|
|
4
4
|
import './index-CvKt4UDE.js';
|
|
@@ -233,7 +233,7 @@ class SocketRequestClient {
|
|
|
233
233
|
const init = async () => {
|
|
234
234
|
// @ts-ignore
|
|
235
235
|
if (!globalThis.WebSocket && !this.#experimentalWebsocket)
|
|
236
|
-
globalThis.WebSocket = (await import('./browser-CCfR9KOM-
|
|
236
|
+
globalThis.WebSocket = (await import('./browser-CCfR9KOM-BGMMnVsR.js').then(function (n) { return n.b; })).default.w3cwebsocket;
|
|
237
237
|
const client = new WebSocket(this.#url, this.#protocol);
|
|
238
238
|
if (this.#experimentalWebsocket) {
|
|
239
239
|
client.addEventListener('error', this.onerror);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MultiWallet, e as encrypt, b as base58$1 } from './node-browser-
|
|
1
|
+
import { M as MultiWallet, e as encrypt, b as base58$1 } from './node-browser-Ha4Lvntj.js';
|
|
2
2
|
import './browser-crypto-CH0ua22L.js';
|
|
3
3
|
import './connection-monitor-D-krNiRH.js';
|
|
4
4
|
import './index-CvKt4UDE.js';
|
|
@@ -524,7 +524,7 @@ const concatAndDoubleHash = async (input) => {
|
|
|
524
524
|
return new Uint8Array(await createDoubleHash(typedArrayConcat(input), 'SHA-256'));
|
|
525
525
|
};
|
|
526
526
|
|
|
527
|
-
const encode$2 = async (source, prefix = new TextEncoder().encode('00'), hex) => {
|
|
527
|
+
const encode$2$1 = async (source, prefix = new TextEncoder().encode('00'), hex) => {
|
|
528
528
|
if (!(source instanceof Uint8Array) || !(prefix instanceof Uint8Array)) {
|
|
529
529
|
throw new TypeError('Expected Uint8Array');
|
|
530
530
|
}
|
|
@@ -568,10 +568,10 @@ const isBase58check = (string, hex) => {
|
|
|
568
568
|
return false;
|
|
569
569
|
}
|
|
570
570
|
};
|
|
571
|
-
const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$2(uint8Array, prefix, true);
|
|
571
|
+
const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$2$1(uint8Array, prefix, true);
|
|
572
572
|
const decodeHex$1 = (string) => decode$3(string, true);
|
|
573
573
|
const isBase58checkHex = (string) => isBase58check(string, true);
|
|
574
|
-
var base58check = { encode: encode$2, decode: decode$3, encodeHex: encodeHex$1, decodeHex: decodeHex$1, isBase58check, isBase58checkHex };
|
|
574
|
+
var base58check = { encode: encode$2$1, decode: decode$3, encodeHex: encodeHex$1, decodeHex: decodeHex$1, isBase58check, isBase58checkHex };
|
|
575
575
|
|
|
576
576
|
/** @leofcoin/binary: dependency-free unsigned framing, with no global byte counters. */
|
|
577
577
|
const MAX_BIGINT = (1n << 256n) - 1n;
|
|
@@ -798,7 +798,7 @@ var index$3 = (input, prefix) => encodeFrames(input.map(asBytes));
|
|
|
798
798
|
|
|
799
799
|
var index$2 = (typedArray, prefix) => decodeFrames(typedArray, 0, { canonical: false });
|
|
800
800
|
|
|
801
|
-
const decode$2 = (multiWif, expectedVersion, expectedCodec) => {
|
|
801
|
+
const decode$2$1 = (multiWif, expectedVersion, expectedCodec) => {
|
|
802
802
|
const decoded = base58$1.decode(multiWif);
|
|
803
803
|
let [version, codec, privateKey] = index$2(decoded);
|
|
804
804
|
version = Number(new TextDecoder().decode(version));
|
|
@@ -817,10 +817,10 @@ var index$1$1 = {
|
|
|
817
817
|
privateKey
|
|
818
818
|
]));
|
|
819
819
|
},
|
|
820
|
-
decode: decode$2,
|
|
820
|
+
decode: decode$2$1,
|
|
821
821
|
isMultiWif: (multiWif) => {
|
|
822
822
|
try {
|
|
823
|
-
const { version, codec, privateKey } = decode$2(multiWif);
|
|
823
|
+
const { version, codec, privateKey } = decode$2$1(multiWif);
|
|
824
824
|
if (version === undefined)
|
|
825
825
|
return false;
|
|
826
826
|
if (codec === undefined)
|
|
@@ -8510,7 +8510,7 @@ class Identity {
|
|
|
8510
8510
|
this.selectedAccount = new TextDecoder().decode(selected);
|
|
8511
8511
|
}
|
|
8512
8512
|
else {
|
|
8513
|
-
const importee = await import(/* webpackChunkName: "generate-account" */ './index-dTQVgkpf-
|
|
8513
|
+
const importee = await import(/* webpackChunkName: "generate-account" */ './index-dTQVgkpf-DOL2rIjp.js');
|
|
8514
8514
|
const { identity, accounts } = await importee.default(password, this.network);
|
|
8515
8515
|
if (fresh) {
|
|
8516
8516
|
await globalThis.accountStore.clear();
|
|
@@ -8573,12 +8573,12 @@ class Identity {
|
|
|
8573
8573
|
}
|
|
8574
8574
|
}
|
|
8575
8575
|
|
|
8576
|
-
const encode$
|
|
8576
|
+
const encode$2 = (string) => {
|
|
8577
8577
|
if (typeof string === 'string')
|
|
8578
8578
|
return new TextEncoder().encode(string);
|
|
8579
8579
|
throw Error(`expected typeof String instead got ${string}`);
|
|
8580
8580
|
};
|
|
8581
|
-
const decode$
|
|
8581
|
+
const decode$2 = (uint8Array) => {
|
|
8582
8582
|
if (uint8Array instanceof Uint8Array)
|
|
8583
8583
|
return new TextDecoder().decode(uint8Array);
|
|
8584
8584
|
throw Error(`expected typeof uint8Array instead got ${uint8Array}`);
|
|
@@ -8589,7 +8589,7 @@ class KeyPath {
|
|
|
8589
8589
|
uint8Array;
|
|
8590
8590
|
constructor(input) {
|
|
8591
8591
|
if (typeof input === 'string') {
|
|
8592
|
-
this.uint8Array = encode$
|
|
8592
|
+
this.uint8Array = encode$2(input);
|
|
8593
8593
|
}
|
|
8594
8594
|
else if (input instanceof Uint8Array) {
|
|
8595
8595
|
this.uint8Array = input;
|
|
@@ -8605,7 +8605,7 @@ class KeyPath {
|
|
|
8605
8605
|
return true;
|
|
8606
8606
|
}
|
|
8607
8607
|
toString() {
|
|
8608
|
-
return decode$
|
|
8608
|
+
return decode$2(this.uint8Array);
|
|
8609
8609
|
}
|
|
8610
8610
|
/**
|
|
8611
8611
|
* Returns the `list` representation of this path.
|
|
@@ -8625,7 +8625,7 @@ class KeyValue {
|
|
|
8625
8625
|
uint8Array;
|
|
8626
8626
|
constructor(input) {
|
|
8627
8627
|
if (typeof input === 'string') {
|
|
8628
|
-
this.uint8Array = encode$
|
|
8628
|
+
this.uint8Array = encode$2(input);
|
|
8629
8629
|
}
|
|
8630
8630
|
else if (input instanceof Uint8Array) {
|
|
8631
8631
|
this.uint8Array = input;
|
|
@@ -8641,7 +8641,7 @@ class KeyValue {
|
|
|
8641
8641
|
return true;
|
|
8642
8642
|
}
|
|
8643
8643
|
toString() {
|
|
8644
|
-
return decode$
|
|
8644
|
+
return decode$2(this.uint8Array);
|
|
8645
8645
|
}
|
|
8646
8646
|
}
|
|
8647
8647
|
|
|
@@ -15734,23 +15734,23 @@ const getTokens = (proto) => {
|
|
|
15734
15734
|
return tokens;
|
|
15735
15735
|
};
|
|
15736
15736
|
|
|
15737
|
-
const encoder = new TextEncoder();
|
|
15737
|
+
const encoder$1 = new TextEncoder();
|
|
15738
15738
|
const toType = (data) => {
|
|
15739
15739
|
if (data instanceof Uint8Array)
|
|
15740
15740
|
return data;
|
|
15741
15741
|
if (data instanceof ArrayBuffer)
|
|
15742
15742
|
return new Uint8Array(data);
|
|
15743
15743
|
if (typeof data === 'bigint')
|
|
15744
|
-
return encoder.encode(data.toString());
|
|
15744
|
+
return encoder$1.encode(data.toString());
|
|
15745
15745
|
if (typeof data === 'string')
|
|
15746
|
-
return encoder.encode(data);
|
|
15746
|
+
return encoder$1.encode(data);
|
|
15747
15747
|
if (typeof data === 'object')
|
|
15748
|
-
return encoder.encode(JSON.stringify(data, jsonStringifyBigInt$1));
|
|
15748
|
+
return encoder$1.encode(JSON.stringify(data, jsonStringifyBigInt$1));
|
|
15749
15749
|
if (typeof data === 'number' || typeof data === 'boolean')
|
|
15750
|
-
return encoder.encode(data.toString());
|
|
15750
|
+
return encoder$1.encode(data.toString());
|
|
15751
15751
|
throw new Error(`unsuported type ${typeof data || data}`);
|
|
15752
15752
|
};
|
|
15753
|
-
const encode = (proto, input, compress) => {
|
|
15753
|
+
const encode$1 = (proto, input, compress) => {
|
|
15754
15754
|
const tokens = getTokens(proto);
|
|
15755
15755
|
const set = [];
|
|
15756
15756
|
for (let i = 0; i < tokens.length; i++) {
|
|
@@ -15807,8 +15807,8 @@ var index$1 = {
|
|
|
15807
15807
|
toString: toString$1};
|
|
15808
15808
|
|
|
15809
15809
|
const { toString } = index$1;
|
|
15810
|
-
const decoder = new TextDecoder();
|
|
15811
|
-
const decode = (proto, uint8Array, compressed) => {
|
|
15810
|
+
const decoder$1 = new TextDecoder();
|
|
15811
|
+
const decode$1 = (proto, uint8Array, compressed) => {
|
|
15812
15812
|
if (compressed)
|
|
15813
15813
|
uint8Array = pako.inflate(uint8Array);
|
|
15814
15814
|
let deconcated = index$2(uint8Array);
|
|
@@ -15823,13 +15823,13 @@ const decode = (proto, uint8Array, compressed) => {
|
|
|
15823
15823
|
else if (token.type === 'string')
|
|
15824
15824
|
output[token.key] = toString(deconcated[i]);
|
|
15825
15825
|
else if (token.type === 'boolean')
|
|
15826
|
-
output[token.key] = decoder.decode(deconcated[i]) === 'true';
|
|
15826
|
+
output[token.key] = decoder$1.decode(deconcated[i]) === 'true';
|
|
15827
15827
|
else if (token.type === 'number')
|
|
15828
|
-
output[token.key] = Number(decoder.decode(deconcated[i]));
|
|
15828
|
+
output[token.key] = Number(decoder$1.decode(deconcated[i]));
|
|
15829
15829
|
else if (token.type === 'bigint')
|
|
15830
|
-
output[token.key] = BigInt(decoder.decode(deconcated[i]));
|
|
15830
|
+
output[token.key] = BigInt(decoder$1.decode(deconcated[i]));
|
|
15831
15831
|
else if (token.type === 'object' || token.type === 'array')
|
|
15832
|
-
output[token.key] = JSON.parse(decoder.decode(deconcated[i]), jsonParseBigInt$1);
|
|
15832
|
+
output[token.key] = JSON.parse(decoder$1.decode(deconcated[i]), jsonParseBigInt$1);
|
|
15833
15833
|
if (token.optional) {
|
|
15834
15834
|
if (!output[token.key] || output[token.key].length === 0)
|
|
15835
15835
|
delete output[token.key];
|
|
@@ -15840,7 +15840,7 @@ const decode = (proto, uint8Array, compressed) => {
|
|
|
15840
15840
|
return output;
|
|
15841
15841
|
};
|
|
15842
15842
|
|
|
15843
|
-
var index = { encode, decode };
|
|
15843
|
+
var index = { encode: encode$1, decode: decode$1 };
|
|
15844
15844
|
|
|
15845
15845
|
const jsonStringifyBigInt = (key, value) => {
|
|
15846
15846
|
if (typeof value === 'bigint')
|
|
@@ -17306,6 +17306,439 @@ class FileTransfer {
|
|
|
17306
17306
|
}
|
|
17307
17307
|
}
|
|
17308
17308
|
|
|
17309
|
+
const VERSION = 1;
|
|
17310
|
+
const MAX_PACKET_BYTES = 64_000;
|
|
17311
|
+
const MAX_ENVELOPE_BYTES = 32_000;
|
|
17312
|
+
const MAX_ARCHIVES = 500;
|
|
17313
|
+
const MAX_OUTBOX = 500;
|
|
17314
|
+
const MAX_AGE = 30 * 24 * 60 * 60 * 1000;
|
|
17315
|
+
const MAX_CLOCK_SKEW = 10 * 60 * 1000;
|
|
17316
|
+
const MAX_HOPS = 8;
|
|
17317
|
+
const encoder = new TextEncoder();
|
|
17318
|
+
const decoder = new TextDecoder();
|
|
17319
|
+
function isUnsubscribe(value) {
|
|
17320
|
+
return typeof value === 'function';
|
|
17321
|
+
}
|
|
17322
|
+
function encode(value) {
|
|
17323
|
+
return encoder.encode(JSON.stringify(value));
|
|
17324
|
+
}
|
|
17325
|
+
function decode(payload) {
|
|
17326
|
+
if (!(payload instanceof Uint8Array))
|
|
17327
|
+
throw new TypeError('Archive packets must be encoded bytes.');
|
|
17328
|
+
if (payload.byteLength > MAX_PACKET_BYTES)
|
|
17329
|
+
throw new RangeError('Archive packet exceeds the size limit.');
|
|
17330
|
+
return JSON.parse(decoder.decode(payload));
|
|
17331
|
+
}
|
|
17332
|
+
function randomId() {
|
|
17333
|
+
if (typeof crypto.randomUUID === 'function')
|
|
17334
|
+
return crypto.randomUUID();
|
|
17335
|
+
return Array.from(crypto.getRandomValues(new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
17336
|
+
}
|
|
17337
|
+
function keyPrefix(network) {
|
|
17338
|
+
return `peernet:archive:v1:${encodeURIComponent(network)}:`;
|
|
17339
|
+
}
|
|
17340
|
+
function recordKey(network, kind, id) {
|
|
17341
|
+
return `${keyPrefix(network)}${kind}:${encodeURIComponent(id)}`;
|
|
17342
|
+
}
|
|
17343
|
+
function stateKey(network) {
|
|
17344
|
+
return `${keyPrefix(network)}state`;
|
|
17345
|
+
}
|
|
17346
|
+
async function storeGet(store, key) {
|
|
17347
|
+
if (store.has && !(await store.has(key)))
|
|
17348
|
+
return undefined;
|
|
17349
|
+
try {
|
|
17350
|
+
return await store.get(key);
|
|
17351
|
+
}
|
|
17352
|
+
catch (error) {
|
|
17353
|
+
if (error?.code === 'LEVEL_NOT_FOUND' ||
|
|
17354
|
+
error?.name === 'NotFoundError')
|
|
17355
|
+
return undefined;
|
|
17356
|
+
throw error;
|
|
17357
|
+
}
|
|
17358
|
+
}
|
|
17359
|
+
function validateEnvelope(envelope, network) {
|
|
17360
|
+
const envelopeNetwork = envelope?.network ||
|
|
17361
|
+
envelope?.senderCertificate?.network ||
|
|
17362
|
+
envelope?.recipientCertificate?.network;
|
|
17363
|
+
if (!envelope ||
|
|
17364
|
+
typeof envelope !== 'object' ||
|
|
17365
|
+
envelopeNetwork !== network ||
|
|
17366
|
+
typeof envelope.id !== 'string' ||
|
|
17367
|
+
!envelope.id ||
|
|
17368
|
+
typeof envelope.recipientAccount !== 'string' ||
|
|
17369
|
+
typeof envelope.recipientDeviceId !== 'string' ||
|
|
17370
|
+
typeof envelope.ciphertext !== 'string' ||
|
|
17371
|
+
!envelope.ciphertext ||
|
|
17372
|
+
typeof envelope.signature !== 'string' ||
|
|
17373
|
+
!Number.isFinite(envelope.timestamp) ||
|
|
17374
|
+
envelope.timestamp < Date.now() - MAX_AGE ||
|
|
17375
|
+
envelope.timestamp > Date.now() + MAX_CLOCK_SKEW)
|
|
17376
|
+
throw new Error('Only a valid, network-scoped encrypted message envelope can be archived.');
|
|
17377
|
+
if ('text' in envelope || 'plaintext' in envelope || 'message' in envelope)
|
|
17378
|
+
throw new Error('Plaintext message fields cannot be archived.');
|
|
17379
|
+
if (encode(envelope).byteLength > MAX_ENVELOPE_BYTES)
|
|
17380
|
+
throw new RangeError('Encrypted message envelope exceeds the archive size limit.');
|
|
17381
|
+
}
|
|
17382
|
+
function validatePacket(value, network) {
|
|
17383
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
17384
|
+
return;
|
|
17385
|
+
const packet = value;
|
|
17386
|
+
if (packet.version !== VERSION ||
|
|
17387
|
+
packet.network !== network ||
|
|
17388
|
+
typeof packet.id !== 'string' ||
|
|
17389
|
+
!packet.id ||
|
|
17390
|
+
typeof packet.origin !== 'string' ||
|
|
17391
|
+
!packet.origin ||
|
|
17392
|
+
!Number.isFinite(packet.timestamp) ||
|
|
17393
|
+
packet.timestamp < Date.now() - MAX_AGE ||
|
|
17394
|
+
packet.timestamp > Date.now() + MAX_CLOCK_SKEW ||
|
|
17395
|
+
!Number.isInteger(packet.hopsRemaining) ||
|
|
17396
|
+
packet.hopsRemaining < 0 ||
|
|
17397
|
+
packet.hopsRemaining > MAX_HOPS ||
|
|
17398
|
+
!['direct', 'inbox-request', 'archive-delivery', 'delivery-receipt'].includes(packet.type) ||
|
|
17399
|
+
!packet.payload ||
|
|
17400
|
+
typeof packet.payload !== 'object' ||
|
|
17401
|
+
Array.isArray(packet.payload))
|
|
17402
|
+
return;
|
|
17403
|
+
return packet;
|
|
17404
|
+
}
|
|
17405
|
+
class PeernetArchiveMode {
|
|
17406
|
+
#peernet;
|
|
17407
|
+
#store;
|
|
17408
|
+
#network;
|
|
17409
|
+
#topic;
|
|
17410
|
+
#verifyEnvelope;
|
|
17411
|
+
#verifyInboxRequest;
|
|
17412
|
+
#verifyReceipt;
|
|
17413
|
+
#handlers = new Set();
|
|
17414
|
+
#seen = new Map();
|
|
17415
|
+
#archives = new Set();
|
|
17416
|
+
#outbox = new Set();
|
|
17417
|
+
#enabled = false;
|
|
17418
|
+
#started = false;
|
|
17419
|
+
#unsubscribe;
|
|
17420
|
+
#retryTimer;
|
|
17421
|
+
#account = '';
|
|
17422
|
+
#deviceId = '';
|
|
17423
|
+
constructor(options) {
|
|
17424
|
+
if (!options?.peernet?.network || typeof options.peernet.publish !== 'function' ||
|
|
17425
|
+
typeof options.peernet.subscribe !== 'function')
|
|
17426
|
+
throw new Error('A Peernet instance is required for archive mode.');
|
|
17427
|
+
const store = options.store || globalThis.messageStore;
|
|
17428
|
+
if (!store || typeof store.get !== 'function' || typeof store.put !== 'function' ||
|
|
17429
|
+
typeof store.delete !== 'function' || typeof store.keys !== 'function')
|
|
17430
|
+
throw new Error('A persistent Peernet message store is required for archive mode.');
|
|
17431
|
+
if (typeof options.verifyEnvelope !== 'function' ||
|
|
17432
|
+
typeof options.verifyInboxRequest !== 'function' ||
|
|
17433
|
+
typeof options.verifyReceipt !== 'function')
|
|
17434
|
+
throw new Error('Archive mode requires envelope, inbox-request and receipt verification functions.');
|
|
17435
|
+
this.#peernet = options.peernet;
|
|
17436
|
+
this.#store = store;
|
|
17437
|
+
this.#network = options.peernet.network;
|
|
17438
|
+
this.#topic = `peernet:archive:v1:${this.#network}`;
|
|
17439
|
+
this.#verifyEnvelope = options.verifyEnvelope;
|
|
17440
|
+
this.#verifyInboxRequest = options.verifyInboxRequest;
|
|
17441
|
+
this.#verifyReceipt = options.verifyReceipt;
|
|
17442
|
+
}
|
|
17443
|
+
get network() {
|
|
17444
|
+
return this.#network;
|
|
17445
|
+
}
|
|
17446
|
+
get enabled() {
|
|
17447
|
+
return this.#enabled;
|
|
17448
|
+
}
|
|
17449
|
+
get archiveCount() {
|
|
17450
|
+
return this.#archives.size;
|
|
17451
|
+
}
|
|
17452
|
+
get pendingCount() {
|
|
17453
|
+
return this.#outbox.size;
|
|
17454
|
+
}
|
|
17455
|
+
onMessage(handler) {
|
|
17456
|
+
if (typeof handler !== 'function')
|
|
17457
|
+
throw new TypeError('An archive message handler is required.');
|
|
17458
|
+
this.#handlers.add(handler);
|
|
17459
|
+
return () => this.#handlers.delete(handler);
|
|
17460
|
+
}
|
|
17461
|
+
async start({ account, deviceId }) {
|
|
17462
|
+
if (this.#started)
|
|
17463
|
+
return;
|
|
17464
|
+
if (!account || !deviceId)
|
|
17465
|
+
throw new Error('A local account and chat device ID are required.');
|
|
17466
|
+
this.#account = account;
|
|
17467
|
+
this.#deviceId = deviceId;
|
|
17468
|
+
await this.#load();
|
|
17469
|
+
const receiver = (payload) => {
|
|
17470
|
+
this.#receive(payload).catch((error) => console.error('Peernet archive packet failed:', error));
|
|
17471
|
+
};
|
|
17472
|
+
const unsubscribe = await this.#peernet.subscribe(this.#topic, receiver);
|
|
17473
|
+
this.#unsubscribe = isUnsubscribe(unsubscribe)
|
|
17474
|
+
? unsubscribe
|
|
17475
|
+
: typeof globalThis.globalSub?.unsubscribe === 'function'
|
|
17476
|
+
? () => globalThis.globalSub.unsubscribe(this.#topic, receiver)
|
|
17477
|
+
: undefined;
|
|
17478
|
+
this.#started = true;
|
|
17479
|
+
this.#retryTimer = setInterval(() => {
|
|
17480
|
+
this.#retryOutbox().catch((error) => console.error('Peernet archive retry failed:', error));
|
|
17481
|
+
}, 30_000);
|
|
17482
|
+
try {
|
|
17483
|
+
for (const id of this.#outbox) {
|
|
17484
|
+
const bytes = await storeGet(this.#store, recordKey(this.#network, 'outbox', id));
|
|
17485
|
+
if (bytes)
|
|
17486
|
+
await this.#publish('direct', { envelope: JSON.parse(decoder.decode(bytes)) });
|
|
17487
|
+
}
|
|
17488
|
+
}
|
|
17489
|
+
catch (error) {
|
|
17490
|
+
this.stop();
|
|
17491
|
+
throw error;
|
|
17492
|
+
}
|
|
17493
|
+
}
|
|
17494
|
+
stop() {
|
|
17495
|
+
clearInterval(this.#retryTimer);
|
|
17496
|
+
this.#retryTimer = undefined;
|
|
17497
|
+
this.#unsubscribe?.();
|
|
17498
|
+
this.#unsubscribe = undefined;
|
|
17499
|
+
this.#started = false;
|
|
17500
|
+
this.#account = '';
|
|
17501
|
+
this.#deviceId = '';
|
|
17502
|
+
}
|
|
17503
|
+
async setEnabled(enabled) {
|
|
17504
|
+
if (typeof enabled !== 'boolean')
|
|
17505
|
+
throw new TypeError('Archive mode must be enabled or disabled explicitly.');
|
|
17506
|
+
if (!this.#started)
|
|
17507
|
+
await this.#load();
|
|
17508
|
+
this.#enabled = enabled;
|
|
17509
|
+
await this.#store.put(stateKey(this.#network), encode({ version: VERSION, enabled }));
|
|
17510
|
+
if (!enabled) {
|
|
17511
|
+
for (const id of this.#archives)
|
|
17512
|
+
await this.#store.delete(recordKey(this.#network, 'archive', id));
|
|
17513
|
+
this.#archives.clear();
|
|
17514
|
+
}
|
|
17515
|
+
}
|
|
17516
|
+
async send(envelope) {
|
|
17517
|
+
validateEnvelope(envelope, this.#network);
|
|
17518
|
+
if (!(await this.#verifyEnvelope(envelope)))
|
|
17519
|
+
throw new Error('Encrypted message signature verification failed.');
|
|
17520
|
+
if (this.#outbox.has(envelope.id))
|
|
17521
|
+
throw new Error('This encrypted message is already pending.');
|
|
17522
|
+
await this.#store.put(recordKey(this.#network, 'outbox', envelope.id), encode(envelope));
|
|
17523
|
+
this.#outbox.add(envelope.id);
|
|
17524
|
+
if (this.#outbox.size > MAX_OUTBOX)
|
|
17525
|
+
await this.#dropOldest('outbox');
|
|
17526
|
+
await this.#publish('direct', { envelope });
|
|
17527
|
+
}
|
|
17528
|
+
async requestInbox(request) {
|
|
17529
|
+
if (request.network !== this.#network ||
|
|
17530
|
+
request.account !== this.#account ||
|
|
17531
|
+
request.deviceId !== this.#deviceId ||
|
|
17532
|
+
!(await this.#validInboxRequest(request)))
|
|
17533
|
+
throw new Error('A valid signed inbox request for this Peernet network and device is required.');
|
|
17534
|
+
await this.#publish('inbox-request', { request });
|
|
17535
|
+
}
|
|
17536
|
+
async acknowledge(receipt) {
|
|
17537
|
+
if (receipt.network !== this.#network ||
|
|
17538
|
+
receipt.recipientAccount !== this.#account ||
|
|
17539
|
+
receipt.recipientDeviceId !== this.#deviceId ||
|
|
17540
|
+
typeof receipt.signature !== 'string' ||
|
|
17541
|
+
!Number.isFinite(receipt.timestamp) ||
|
|
17542
|
+
Math.abs(Date.now() - receipt.timestamp) > MAX_CLOCK_SKEW)
|
|
17543
|
+
throw new Error('Delivery receipt does not belong to this Peernet network and device.');
|
|
17544
|
+
await this.#publish('delivery-receipt', { receipt });
|
|
17545
|
+
}
|
|
17546
|
+
async #load() {
|
|
17547
|
+
const state = await storeGet(this.#store, stateKey(this.#network));
|
|
17548
|
+
if (state) {
|
|
17549
|
+
const saved = JSON.parse(decoder.decode(state));
|
|
17550
|
+
if (saved?.version !== VERSION || typeof saved.enabled !== 'boolean')
|
|
17551
|
+
throw new Error('Saved Peernet archive settings are invalid.');
|
|
17552
|
+
this.#enabled = saved.enabled;
|
|
17553
|
+
}
|
|
17554
|
+
const prefix = keyPrefix(this.#network);
|
|
17555
|
+
const keys = await this.#store.keys();
|
|
17556
|
+
for (const keyInput of keys) {
|
|
17557
|
+
const key = keyInput.toString().replace(/^\/+/, '');
|
|
17558
|
+
if (!key.startsWith(prefix))
|
|
17559
|
+
continue;
|
|
17560
|
+
const archiveMarker = `${prefix}archive:`;
|
|
17561
|
+
const outboxMarker = `${prefix}outbox:`;
|
|
17562
|
+
if (key.startsWith(archiveMarker))
|
|
17563
|
+
this.#archives.add(decodeURIComponent(key.slice(archiveMarker.length)));
|
|
17564
|
+
if (key.startsWith(outboxMarker))
|
|
17565
|
+
this.#outbox.add(decodeURIComponent(key.slice(outboxMarker.length)));
|
|
17566
|
+
}
|
|
17567
|
+
await this.#prune();
|
|
17568
|
+
}
|
|
17569
|
+
async #publish(type, payload, hopsRemaining = MAX_HOPS) {
|
|
17570
|
+
if (!this.#started)
|
|
17571
|
+
throw new Error('Peernet archive mode is not started.');
|
|
17572
|
+
const packet = {
|
|
17573
|
+
version: VERSION,
|
|
17574
|
+
id: randomId(),
|
|
17575
|
+
network: this.#network,
|
|
17576
|
+
origin: this.#peernet.id,
|
|
17577
|
+
timestamp: Date.now(),
|
|
17578
|
+
hopsRemaining,
|
|
17579
|
+
type,
|
|
17580
|
+
payload
|
|
17581
|
+
};
|
|
17582
|
+
const bytes = encode(packet);
|
|
17583
|
+
if (bytes.byteLength > MAX_PACKET_BYTES)
|
|
17584
|
+
throw new RangeError('Peernet archive packet exceeds its size limit.');
|
|
17585
|
+
this.#seen.set(packet.id, packet.timestamp);
|
|
17586
|
+
await this.#peernet.publish(this.#topic, bytes);
|
|
17587
|
+
}
|
|
17588
|
+
async #receive(payload) {
|
|
17589
|
+
const packet = validatePacket(decode(payload), this.#network);
|
|
17590
|
+
if (!packet || packet.origin === this.#peernet.id || this.#wasSeen(packet.id))
|
|
17591
|
+
return;
|
|
17592
|
+
this.#markSeen(packet.id);
|
|
17593
|
+
let forward = false;
|
|
17594
|
+
switch (packet.type) {
|
|
17595
|
+
case 'direct':
|
|
17596
|
+
case 'archive-delivery': {
|
|
17597
|
+
const envelope = packet.payload.envelope;
|
|
17598
|
+
try {
|
|
17599
|
+
validateEnvelope(envelope, this.#network);
|
|
17600
|
+
}
|
|
17601
|
+
catch {
|
|
17602
|
+
break;
|
|
17603
|
+
}
|
|
17604
|
+
if (!(await this.#verifyEnvelope(envelope)))
|
|
17605
|
+
break;
|
|
17606
|
+
forward = true;
|
|
17607
|
+
if (packet.type === 'direct' && this.#enabled)
|
|
17608
|
+
await this.#saveArchive(envelope);
|
|
17609
|
+
if (envelope.recipientAccount === this.#account && envelope.recipientDeviceId === this.#deviceId) {
|
|
17610
|
+
for (const handler of this.#handlers)
|
|
17611
|
+
handler({ type: 'message', envelope });
|
|
17612
|
+
}
|
|
17613
|
+
break;
|
|
17614
|
+
}
|
|
17615
|
+
case 'inbox-request': {
|
|
17616
|
+
const request = packet.payload.request;
|
|
17617
|
+
if (!(await this.#validInboxRequest(request)))
|
|
17618
|
+
break;
|
|
17619
|
+
forward = true;
|
|
17620
|
+
if (this.#enabled && !(request.account === this.#account && request.deviceId === this.#deviceId))
|
|
17621
|
+
await this.#deliverTo(request);
|
|
17622
|
+
break;
|
|
17623
|
+
}
|
|
17624
|
+
case 'delivery-receipt': {
|
|
17625
|
+
const receipt = packet.payload.receipt;
|
|
17626
|
+
forward = await this.#receiveReceipt(receipt);
|
|
17627
|
+
break;
|
|
17628
|
+
}
|
|
17629
|
+
}
|
|
17630
|
+
if (forward && packet.hopsRemaining > 0) {
|
|
17631
|
+
await this.#peernet.publish(this.#topic, encode({ ...packet, hopsRemaining: packet.hopsRemaining - 1 }));
|
|
17632
|
+
}
|
|
17633
|
+
}
|
|
17634
|
+
async #validInboxRequest(request) {
|
|
17635
|
+
return Boolean(request &&
|
|
17636
|
+
request.network === this.#network &&
|
|
17637
|
+
typeof request.account === 'string' &&
|
|
17638
|
+
typeof request.deviceId === 'string' &&
|
|
17639
|
+
Number.isFinite(request.timestamp) &&
|
|
17640
|
+
Math.abs(Date.now() - request.timestamp) <= MAX_CLOCK_SKEW &&
|
|
17641
|
+
typeof request.signature === 'string' &&
|
|
17642
|
+
await this.#verifyInboxRequest(request));
|
|
17643
|
+
}
|
|
17644
|
+
async #saveArchive(envelope) {
|
|
17645
|
+
if (this.#archives.has(envelope.id))
|
|
17646
|
+
return;
|
|
17647
|
+
await this.#store.put(recordKey(this.#network, 'archive', envelope.id), encode({
|
|
17648
|
+
envelope,
|
|
17649
|
+
storedAt: Date.now()
|
|
17650
|
+
}));
|
|
17651
|
+
this.#archives.add(envelope.id);
|
|
17652
|
+
if (this.#archives.size > MAX_ARCHIVES)
|
|
17653
|
+
await this.#dropOldest('archive');
|
|
17654
|
+
}
|
|
17655
|
+
async #deliverTo(request) {
|
|
17656
|
+
for (const id of this.#archives) {
|
|
17657
|
+
const bytes = await storeGet(this.#store, recordKey(this.#network, 'archive', id));
|
|
17658
|
+
if (!bytes) {
|
|
17659
|
+
this.#archives.delete(id);
|
|
17660
|
+
continue;
|
|
17661
|
+
}
|
|
17662
|
+
const record = JSON.parse(decoder.decode(bytes));
|
|
17663
|
+
const envelope = record?.envelope;
|
|
17664
|
+
if (envelope?.recipientAccount === request.account && envelope.recipientDeviceId === request.deviceId)
|
|
17665
|
+
await this.#publish('archive-delivery', { envelope });
|
|
17666
|
+
}
|
|
17667
|
+
}
|
|
17668
|
+
async #receiveReceipt(receipt) {
|
|
17669
|
+
if (!receipt || receipt.network !== this.#network ||
|
|
17670
|
+
typeof receipt.messageId !== 'string' ||
|
|
17671
|
+
!Number.isFinite(receipt.timestamp) ||
|
|
17672
|
+
Math.abs(Date.now() - receipt.timestamp) > MAX_CLOCK_SKEW)
|
|
17673
|
+
return false;
|
|
17674
|
+
const keys = ['archive', 'outbox'];
|
|
17675
|
+
let verified = false;
|
|
17676
|
+
for (const kind of keys) {
|
|
17677
|
+
const key = recordKey(this.#network, kind, receipt.messageId);
|
|
17678
|
+
const bytes = await storeGet(this.#store, key);
|
|
17679
|
+
if (!bytes)
|
|
17680
|
+
continue;
|
|
17681
|
+
const stored = JSON.parse(decoder.decode(bytes));
|
|
17682
|
+
const envelope = kind === 'archive' ? stored.envelope : stored;
|
|
17683
|
+
if (!(await this.#verifyReceipt(receipt, envelope)))
|
|
17684
|
+
continue;
|
|
17685
|
+
await this.#store.delete(key);
|
|
17686
|
+
(kind === 'archive' ? this.#archives : this.#outbox).delete(receipt.messageId);
|
|
17687
|
+
verified = true;
|
|
17688
|
+
}
|
|
17689
|
+
return verified;
|
|
17690
|
+
}
|
|
17691
|
+
async #retryOutbox() {
|
|
17692
|
+
if (!this.#started)
|
|
17693
|
+
return;
|
|
17694
|
+
await this.#prune();
|
|
17695
|
+
for (const id of this.#outbox) {
|
|
17696
|
+
const bytes = await storeGet(this.#store, recordKey(this.#network, 'outbox', id));
|
|
17697
|
+
if (bytes)
|
|
17698
|
+
await this.#publish('direct', { envelope: JSON.parse(decoder.decode(bytes)) });
|
|
17699
|
+
}
|
|
17700
|
+
}
|
|
17701
|
+
async #prune() {
|
|
17702
|
+
const cutoff = Date.now() - MAX_AGE;
|
|
17703
|
+
for (const [kind, ids] of [['archive', this.#archives], ['outbox', this.#outbox]]) {
|
|
17704
|
+
for (const id of ids) {
|
|
17705
|
+
const key = recordKey(this.#network, kind, id);
|
|
17706
|
+
const bytes = await storeGet(this.#store, key);
|
|
17707
|
+
if (!bytes) {
|
|
17708
|
+
ids.delete(id);
|
|
17709
|
+
continue;
|
|
17710
|
+
}
|
|
17711
|
+
const saved = JSON.parse(decoder.decode(bytes));
|
|
17712
|
+
const timestamp = kind === 'archive' ? saved.storedAt : saved.timestamp;
|
|
17713
|
+
if (!Number.isFinite(timestamp) || timestamp < cutoff) {
|
|
17714
|
+
await this.#store.delete(key);
|
|
17715
|
+
ids.delete(id);
|
|
17716
|
+
}
|
|
17717
|
+
}
|
|
17718
|
+
}
|
|
17719
|
+
}
|
|
17720
|
+
async #dropOldest(kind) {
|
|
17721
|
+
const ids = kind === 'archive' ? this.#archives : this.#outbox;
|
|
17722
|
+
const id = ids.values().next().value;
|
|
17723
|
+
if (!id)
|
|
17724
|
+
return;
|
|
17725
|
+
await this.#store.delete(recordKey(this.#network, kind, id));
|
|
17726
|
+
ids.delete(id);
|
|
17727
|
+
}
|
|
17728
|
+
#wasSeen(id) {
|
|
17729
|
+
const timestamp = this.#seen.get(id);
|
|
17730
|
+
return timestamp !== undefined && Date.now() - timestamp < MAX_AGE;
|
|
17731
|
+
}
|
|
17732
|
+
#markSeen(id) {
|
|
17733
|
+
this.#seen.set(id, Date.now());
|
|
17734
|
+
if (this.#seen.size > 4_000) {
|
|
17735
|
+
const oldestId = this.#seen.keys().next().value;
|
|
17736
|
+
if (oldestId !== undefined)
|
|
17737
|
+
this.#seen.delete(oldestId);
|
|
17738
|
+
}
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
|
|
17309
17742
|
const DEFAULT_CHUNK_SIZE = 256 * 1024;
|
|
17310
17743
|
const DEFAULT_BLOCK_CHUNK_SIZE = 256 * 1024;
|
|
17311
17744
|
const DEFAULT_BLOCK_CHUNK_THRESHOLD = 1024 * 1024;
|
|
@@ -17332,7 +17765,7 @@ class Peernet {
|
|
|
17332
17765
|
root;
|
|
17333
17766
|
identity;
|
|
17334
17767
|
stores = [];
|
|
17335
|
-
|
|
17768
|
+
routingId;
|
|
17336
17769
|
/**
|
|
17337
17770
|
* @type {Object}
|
|
17338
17771
|
* @property {Object} peer Instance of Peer
|
|
@@ -17367,6 +17800,7 @@ class Peernet {
|
|
|
17367
17800
|
* @param {String} options.root - path to root directory
|
|
17368
17801
|
* @param {String} options.version - path to root directory
|
|
17369
17802
|
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
17803
|
+
* @param {String} options.routingId - optional transport identity, separate from the wallet identity
|
|
17370
17804
|
* @param {Object} options.transport - transport selection and fallback options passed to @netpeer/swarm
|
|
17371
17805
|
*
|
|
17372
17806
|
* @return {Promise} instance of Peernet
|
|
@@ -17379,6 +17813,9 @@ class Peernet {
|
|
|
17379
17813
|
* @property {String} network - current network
|
|
17380
17814
|
*/
|
|
17381
17815
|
this.network = options.network || 'leofcoin';
|
|
17816
|
+
if (options.routingId !== undefined && (typeof options.routingId !== 'string' || !options.routingId.trim()))
|
|
17817
|
+
throw new TypeError('routingId must be a non-empty string');
|
|
17818
|
+
this.routingId = options.routingId?.trim();
|
|
17382
17819
|
this.autoStart = options.autoStart === undefined ? true : options.autoStart;
|
|
17383
17820
|
this.stars = options.stars;
|
|
17384
17821
|
this.transport = options.transport;
|
|
@@ -17410,6 +17847,9 @@ class Peernet {
|
|
|
17410
17847
|
return this._init(options, password);
|
|
17411
17848
|
}
|
|
17412
17849
|
get id() {
|
|
17850
|
+
return this.routingId || this.identity.id;
|
|
17851
|
+
}
|
|
17852
|
+
get peerId() {
|
|
17413
17853
|
return this.identity.id;
|
|
17414
17854
|
}
|
|
17415
17855
|
get selectedAccount() {
|
|
@@ -17431,6 +17871,9 @@ class Peernet {
|
|
|
17431
17871
|
addCodec(codec) {
|
|
17432
17872
|
return utils.addCodec(codec);
|
|
17433
17873
|
}
|
|
17874
|
+
createArchiveMode(options) {
|
|
17875
|
+
return new PeernetArchiveMode({ ...options, peernet: this });
|
|
17876
|
+
}
|
|
17434
17877
|
async addStore(name, prefix, root, isPrivate = true) {
|
|
17435
17878
|
if (name === 'block' ||
|
|
17436
17879
|
name === 'transaction' ||
|
|
@@ -17484,7 +17927,7 @@ class Peernet {
|
|
|
17484
17927
|
await getAddress();
|
|
17485
17928
|
this.storePrefix = options.storePrefix;
|
|
17486
17929
|
this.root = options.root;
|
|
17487
|
-
const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile } = await import(/* webpackChunkName: "messages" */ './messages-
|
|
17930
|
+
const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile } = await import(/* webpackChunkName: "messages" */ './messages-DCePyqKC-BSvJxDWZ.js');
|
|
17488
17931
|
/**
|
|
17489
17932
|
* proto Object containing protos
|
|
17490
17933
|
* @type {Object}
|
|
@@ -17517,7 +17960,6 @@ class Peernet {
|
|
|
17517
17960
|
this.identity = new Identity(this.network);
|
|
17518
17961
|
await this.identity.load(password, options.freshIdentity === true);
|
|
17519
17962
|
this._peerHandler = new PeerDiscovery(this.id);
|
|
17520
|
-
this.peerId = this.id;
|
|
17521
17963
|
/**
|
|
17522
17964
|
* converts data -> message -> proto
|
|
17523
17965
|
* @see DataHandler
|
|
@@ -17531,7 +17973,7 @@ class Peernet {
|
|
|
17531
17973
|
if (this.#starting || this.#started)
|
|
17532
17974
|
return;
|
|
17533
17975
|
this.#starting = true;
|
|
17534
|
-
const importee = await import('./client-
|
|
17976
|
+
const importee = await import('./client-DKKzPSlb-C_1ug6ux.js');
|
|
17535
17977
|
/**
|
|
17536
17978
|
* @access public
|
|
17537
17979
|
* @type {PeernetClient}
|