@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.
@@ -1,4 +1,4 @@
1
- import { g as getDefaultExportFromCjs } from './node-browser-BNe-en1l.js';
1
+ import { g as getDefaultExportFromCjs } from './node-browser-Ha4Lvntj.js';
2
2
  import './browser-crypto-CH0ua22L.js';
3
3
  import './index-CvKt4UDE.js';
4
4
  import './sha2-BKcEJzgd.js';
@@ -1,4 +1,4 @@
1
- import { i as inflate_1, c as createDebugger, d as deflate_1, L as LittlePubSub } from './node-browser-BNe-en1l.js';
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-CpStHgTj.js').then(function (n) { return n.b; })).default.w3cwebsocket;
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-BNe-en1l.js';
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';
@@ -1,4 +1,4 @@
1
- import { F as FormatInterface } from './node-browser-BNe-en1l.js';
1
+ import { F as FormatInterface } 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-BbdshWB0.js');
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$1 = (string) => {
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$1 = (uint8Array) => {
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$1(input);
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$1(this.uint8Array);
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$1(input);
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$1(this.uint8Array);
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
- peerId;
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-BGHaihSi-eHZVl0y9.js');
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-C_pBoi1g-_pfKzCvX.js');
17976
+ const importee = await import('./client-DKKzPSlb-C_1ug6ux.js');
17535
17977
  /**
17536
17978
  * @access public
17537
17979
  * @type {PeernetClient}
@@ -1,4 +1,4 @@
1
- export { N as default } from './node-browser-BNe-en1l.js';
1
+ export { N as default } 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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/chain",
3
- "version": "1.10.19",
3
+ "version": "1.10.20",
4
4
  "description": "Official javascript implementation",
5
5
  "private": false,
6
6
  "exports": {