@leofcoin/chain 1.10.15 → 1.10.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/exports/browser/beacon-wire.js +2 -2
  2. package/exports/browser/{browser-DvU1xNFS-DmmNUdE-.js → browser-CCfR9KOM-CvhDZrGu.js} +3 -3
  3. package/exports/browser/{browser-crypto-B9N9KlL4.js → browser-crypto-CeE-yId5.js} +283 -158
  4. package/exports/browser/chain.js +28 -11
  5. package/exports/browser/{client-DDC8C510-3uaB70KL.js → client-CAC7RVMf-Dmz_2A_5.js} +4 -4
  6. package/exports/browser/{connection-monitor-GPztgSSJ.js → connection-monitor-Cmv00mwl.js} +4 -4
  7. package/exports/browser/{index-Ccn5sExe.js → index-BC-KRBgE.js} +55 -52
  8. package/exports/browser/{index-Bxr5Iztg-WbFIosyf.js → index-dTQVgkpf-CuHJWO2l.js} +3 -3
  9. package/exports/browser/{messages-DH0tgNM9-C2CtyLsf.js → messages-Dw86z0zq-DrPw_nlZ.js} +9 -4
  10. package/exports/browser/{node-browser-BYi7D_oa.js → node-browser-DWSdi-XP.js} +950 -482
  11. package/exports/browser/node-browser.js +3 -3
  12. package/exports/browser/workers/block-worker.js +5 -3
  13. package/exports/browser/workers/machine-worker.js +8 -8
  14. package/exports/{workers/worker-DMCj1e6z-CTYVa2yX.js → browser/workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
  15. package/exports/chain.js +10 -5
  16. package/exports/{constants-CMYKv-Rt.js → constants-Dy9i9S_o.js} +1 -1
  17. package/exports/hash-format-state.js +17 -0
  18. package/exports/node.js +1 -1
  19. package/exports/workers/block-worker.js +5 -3
  20. package/exports/workers/machine-worker.js +8 -8
  21. package/exports/{browser/workers/worker-DMCj1e6z-CTYVa2yX.js → workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
  22. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
- import { F as FormatInterface$2, T as TransactionMessage, C as ContractMessage, n as createHash$1 } from './browser-crypto-B9N9KlL4.js';
2
- import { B as BlockMessage, a as BWMessage, V as ValidatorMessage, S as StateMessage, L as LastBlockMessage, D as DEFAULT_NODE_OPTIONS, g as guardPeerMessages } from './connection-monitor-GPztgSSJ.js';
1
+ import { F as FormatInterface$2, T as TransactionMessage, C as ContractMessage, n as createHash$1 } from './browser-crypto-CeE-yId5.js';
2
+ import { B as BlockMessage, a as BWMessage, V as ValidatorMessage, S as StateMessage, L as LastBlockMessage, D as DEFAULT_NODE_OPTIONS, g as guardPeerMessages } from './connection-monitor-Cmv00mwl.js';
3
3
  import './index-CvKt4UDE.js';
4
4
 
5
5
  var proto = {};
@@ -398,13 +398,13 @@ const ALPHABET$1$1 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
398
398
  const ALPHABET_HEX$1 = "0123456789ABCDEFGHJKLMNPQRSTUVabcdefghijklmnopqrstuv";
399
399
  const base58 = base(ALPHABET$1$1);
400
400
  const base58Hex = base(ALPHABET_HEX$1);
401
- const encode$4 = base58.encode;
402
- const decode$5 = base58.decode;
401
+ const encode$3 = base58.encode;
402
+ const decode$4 = base58.decode;
403
403
  const encodeHex$2 = base58Hex.encode;
404
404
  const decodeHex$2 = base58Hex.decode;
405
405
  const isBase58 = (string) => {
406
406
  try {
407
- decode$5(string);
407
+ decode$4(string);
408
408
  return true;
409
409
  }
410
410
  catch (e) {
@@ -422,7 +422,7 @@ const isBase58Hex = (string) => {
422
422
  };
423
423
  const whatType = (string) => {
424
424
  try {
425
- decode$5(string);
425
+ decode$4(string);
426
426
  return "base58";
427
427
  }
428
428
  catch (e) {
@@ -436,8 +436,8 @@ const whatType = (string) => {
436
436
  }
437
437
  };
438
438
  var base58$1 = {
439
- encode: encode$4,
440
- decode: decode$5,
439
+ encode: encode$3,
440
+ decode: decode$4,
441
441
  isBase58,
442
442
  isBase58Hex,
443
443
  encodeHex: encodeHex$2,
@@ -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$3 = async (source, prefix = new TextEncoder().encode('00'), hex) => {
527
+ const encode$2 = 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
  }
@@ -541,7 +541,7 @@ const encode$3 = async (source, prefix = new TextEncoder().encode('00'), hex) =>
541
541
  return base58$1.encodeHex(uint8Array);
542
542
  return base58$1.encode(uint8Array);
543
543
  };
544
- const decode$4 = async (string, hex) => {
544
+ const decode$3 = async (string, hex) => {
545
545
  let uint8Array = hex ? base58$1.decodeHex(string) : base58$1.decode(string);
546
546
  const prefix = uint8Array.subarray(0, 2);
547
547
  const source = uint8Array.subarray(2, -4);
@@ -561,129 +561,242 @@ const decode$4 = async (string, hex) => {
561
561
  };
562
562
  const isBase58check = (string, hex) => {
563
563
  try {
564
- hex ? decode$4(string, true) : decode$4(string);
564
+ hex ? decode$3(string, true) : decode$3(string);
565
565
  return true;
566
566
  }
567
567
  catch (e) {
568
568
  return false;
569
569
  }
570
570
  };
571
- const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$3(uint8Array, prefix, true);
572
- const decodeHex$1 = (string) => decode$4(string, true);
571
+ const encodeHex$1 = (uint8Array, prefix = new TextEncoder().encode('00')) => encode$2(uint8Array, prefix, true);
572
+ const decodeHex$1 = (string) => decode$3(string, true);
573
573
  const isBase58checkHex = (string) => isBase58check(string, true);
574
- var base58check = { encode: encode$3, decode: decode$4, encodeHex: encodeHex$1, decodeHex: decodeHex$1, isBase58check, isBase58checkHex };
575
-
576
- const MSB$1 = 0x80;
577
- const MSBALL = -128;
578
- const INT = Math.pow(2, 31);
579
- const encode$2 = (num, out, offset) => {
580
- if (Number.MAX_SAFE_INTEGER && num > Number.MAX_SAFE_INTEGER) {
581
- encode$2.bytes = 0;
582
- throw new RangeError('Could not encode varint');
583
- }
584
- out = out || [];
585
- offset = offset || 0;
586
- const oldOffset = offset;
587
- while (num >= INT) {
588
- out[offset++] = (num & 0xFF) | MSB$1;
589
- num /= 128;
590
- }
591
- while (num & MSBALL) {
592
- out[offset++] = (num & 0xFF) | MSB$1;
593
- num >>>= 7;
594
- }
595
- out[offset] = num | 0;
596
- encode$2.bytes = offset - oldOffset + 1;
597
- return out;
598
- };
574
+ var base58check = { encode: encode$2, decode: decode$3, encodeHex: encodeHex$1, decodeHex: decodeHex$1, isBase58check, isBase58checkHex };
599
575
 
600
- const MSB = 0x80;
601
- const REST = 0x7F;
602
- const decode$3 = (buf, offset) => {
603
- offset = offset || 0;
604
- const l = buf.length;
605
- let counter = offset;
606
- let result = 0;
607
- let shift = 0;
608
- let b;
609
- do {
610
- if (counter >= l || shift > 49) {
611
- decode$3.bytes = 0;
612
- throw new RangeError('Could not decode varint');
576
+ /** @leofcoin/binary: dependency-free unsigned framing, with no global byte counters. */
577
+ const MAX_BIGINT = (1n << 256n) - 1n;
578
+ function size(value) {
579
+ if (!Number.isSafeInteger(value) || value < 0)
580
+ throw new RangeError('Expected an unsigned safe integer');
581
+ }
582
+ function bytes(value) {
583
+ if (!(value instanceof Uint8Array))
584
+ throw new TypeError('Expected Uint8Array');
585
+ }
586
+ function varUintLength(value) {
587
+ size(value);
588
+ let length = 1;
589
+ while (value >= 128) {
590
+ value = Math.floor(value / 128);
591
+ length++;
592
+ }
593
+ return length;
594
+ }
595
+ function varBigIntLength(value) {
596
+ if (typeof value !== 'bigint')
597
+ throw new TypeError('Expected BigInt');
598
+ if (value < 0n || value > MAX_BIGINT)
599
+ throw new RangeError('Expected an unsigned 256-bit integer');
600
+ let length = 1;
601
+ while (value >= 128n) {
602
+ value >>= 7n;
603
+ length++;
604
+ }
605
+ return length;
606
+ }
607
+ /** Fixed-capacity writer. finish() is a view; copy it before reusing/resetting the writer. */
608
+ class BinaryWriter {
609
+ #buffer;
610
+ #position = 0;
611
+ constructor(capacity = 64) {
612
+ if (typeof capacity === 'number') {
613
+ size(capacity);
614
+ this.#buffer = new Uint8Array(capacity);
613
615
  }
614
- b = buf[counter++];
615
- result += shift < 28
616
- ? (b & REST) << shift
617
- : (b & REST) * Math.pow(2, shift);
618
- shift += 7;
619
- } while (b >= MSB);
620
- decode$3.bytes = counter - offset;
621
- return result;
622
- };
623
-
624
- const N1 = Math.pow(2, 7);
625
- const N2 = Math.pow(2, 14);
626
- const N3 = Math.pow(2, 21);
627
- const N4 = Math.pow(2, 28);
628
- const N5 = Math.pow(2, 35);
629
- const N6 = Math.pow(2, 42);
630
- const N7 = Math.pow(2, 49);
631
- const N8 = Math.pow(2, 56);
632
- const N9 = Math.pow(2, 63);
633
- var encodingLength = (value) => (value < N1 ? 1
634
- : value < N2 ? 2
635
- : value < N3 ? 3
636
- : value < N4 ? 4
637
- : value < N5 ? 5
638
- : value < N6 ? 6
639
- : value < N7 ? 7
640
- : value < N8 ? 8
641
- : value < N9 ? 9
642
- : 10);
643
-
644
- var index$4 = {
645
- encode: encode$2,
646
- decode: decode$3,
647
- encodingLength
648
- };
616
+ else {
617
+ bytes(capacity);
618
+ this.#buffer = capacity;
619
+ }
620
+ }
621
+ get position() {
622
+ return this.#position;
623
+ }
624
+ get remaining() {
625
+ return this.#buffer.length - this.#position;
626
+ }
627
+ #require(length) {
628
+ size(length);
629
+ if (length > this.remaining)
630
+ throw new RangeError('Binary output buffer is too small');
631
+ }
632
+ writeVarUint(value) {
633
+ this.#require(varUintLength(value));
634
+ this.#writeVarUint(value);
635
+ return this;
636
+ }
637
+ #writeVarUint(value) {
638
+ while (value >= 128) {
639
+ this.#buffer[this.#position++] = value % 128 | 128;
640
+ value = Math.floor(value / 128);
641
+ }
642
+ this.#buffer[this.#position++] = value;
643
+ }
644
+ writeVarBigInt(value) {
645
+ this.#require(varBigIntLength(value));
646
+ do {
647
+ const byte = Number(value & 127n);
648
+ value >>= 7n;
649
+ this.#buffer[this.#position++] = byte | (value ? 128 : 0);
650
+ } while (value);
651
+ return this;
652
+ }
653
+ writeBytes(value) {
654
+ bytes(value);
655
+ this.#require(value.length);
656
+ this.#buffer.set(value, this.#position);
657
+ this.#position += value.length;
658
+ return this;
659
+ }
660
+ writeFrame(value) {
661
+ bytes(value);
662
+ // Check the complete operation before writing its length prefix.
663
+ const prefixLength = varUintLength(value.length);
664
+ this.#require(prefixLength + value.length);
665
+ const prefixStart = this.#buffer.byteOffset + this.#position;
666
+ if (value.buffer === this.#buffer.buffer &&
667
+ value.byteOffset < prefixStart + prefixLength &&
668
+ value.byteOffset + value.length > prefixStart)
669
+ throw new RangeError('Frame source overlaps its length prefix');
670
+ this.#writeVarUint(value.length);
671
+ this.#buffer.set(value, this.#position);
672
+ this.#position += value.length;
673
+ return this;
674
+ }
675
+ reset() {
676
+ this.#position = 0;
677
+ return this;
678
+ }
679
+ finish(copy = false) {
680
+ const result = this.#buffer.subarray(0, this.#position);
681
+ // Buffer.slice() aliases memory; this constructor copies every Uint8Array subtype.
682
+ return copy ? new Uint8Array(result) : result;
683
+ }
684
+ }
685
+ /** Reads views of the original buffer. Failed operations leave the cursor unchanged. */
686
+ class BinaryReader {
687
+ #buffer;
688
+ #position = 0;
689
+ constructor(buffer) {
690
+ bytes(buffer);
691
+ this.#buffer = buffer;
692
+ }
693
+ get position() {
694
+ return this.#position;
695
+ }
696
+ get remaining() {
697
+ return this.#buffer.length - this.#position;
698
+ }
699
+ readVarUint(canonical = true) {
700
+ let result = 0;
701
+ let multiplier = 1;
702
+ for (let index = 0; index < 8; index++) {
703
+ const cursor = this.#position + index;
704
+ if (cursor >= this.#buffer.length)
705
+ throw new RangeError('Truncated varint');
706
+ const byte = this.#buffer[cursor];
707
+ const payload = byte & 127;
708
+ if (index === 7 && payload > Math.floor((Number.MAX_SAFE_INTEGER - result) / multiplier))
709
+ throw new RangeError('Varint exceeds safe integer range');
710
+ result += payload * multiplier;
711
+ if (byte < 128) {
712
+ if (canonical && index > 0 && payload === 0)
713
+ throw new RangeError('Non-canonical varint');
714
+ this.#position = cursor + 1;
715
+ return result;
716
+ }
717
+ multiplier *= 128;
718
+ }
719
+ throw new RangeError('Varint exceeds safe integer range');
720
+ }
721
+ readVarBigInt(canonical = true) {
722
+ let result = 0n;
723
+ for (let index = 0; index < 37; index++) {
724
+ const cursor = this.#position + index;
725
+ if (cursor >= this.#buffer.length)
726
+ throw new RangeError('Truncated varint');
727
+ const byte = this.#buffer[cursor];
728
+ result |= BigInt(byte & 127) << BigInt(index * 7);
729
+ if (result > MAX_BIGINT)
730
+ throw new RangeError('Varint exceeds 256 bits');
731
+ if (byte < 128) {
732
+ if (canonical && index > 0 && byte === 0)
733
+ throw new RangeError('Non-canonical varint');
734
+ this.#position = cursor + 1;
735
+ return result;
736
+ }
737
+ }
738
+ throw new RangeError('Varint exceeds 256 bits');
739
+ }
740
+ readBytes(length) {
741
+ size(length);
742
+ if (length > this.remaining)
743
+ throw new RangeError('Truncated binary field');
744
+ const start = this.#position;
745
+ this.#position += length;
746
+ return this.#buffer.subarray(start, this.#position);
747
+ }
748
+ readFrame(canonical = true) {
749
+ const start = this.#position;
750
+ try {
751
+ return this.readBytes(this.readVarUint(canonical));
752
+ }
753
+ catch (error) {
754
+ this.#position = start;
755
+ throw error;
756
+ }
757
+ }
758
+ }
759
+ /** Existing smart-concat bytes, with exactly one output allocation and no prefix arrays. */
760
+ function encodeFrames(frames, prefix) {
761
+ if (!Array.isArray(frames))
762
+ throw new TypeError('Expected an array of byte frames');
763
+ let length = 0;
764
+ for (const frame of frames) {
765
+ bytes(frame);
766
+ length += varUintLength(frame.length) + frame.length;
767
+ size(length);
768
+ }
769
+ const writer = new BinaryWriter(length);
770
+ for (const frame of frames)
771
+ writer.writeFrame(frame);
772
+ return writer.finish();
773
+ }
774
+ /** Iterative framing: bounded by the input length, not recursive stack depth. */
775
+ function decodeFrames(buffer, prefixLength = 0, options = {}) {
776
+ const reader = new BinaryReader(buffer);
777
+ reader.readBytes(prefixLength);
778
+ const maxFrames = options.maxFrames ?? buffer.length;
779
+ size(maxFrames);
780
+ const frames = [];
781
+ while (reader.remaining) {
782
+ if (frames.length >= maxFrames)
783
+ throw new RangeError('Too many binary frames');
784
+ frames.push(reader.readFrame(options.canonical ?? true));
785
+ }
786
+ return frames;
787
+ }
649
788
 
650
- var index$3 = (input, prefix) => {
651
- const encodedArray = [];
652
- const length = input.reduce((total, current) => {
653
- const encoded = index$4.encode(current.length);
654
- encodedArray.push(encoded);
655
- total += current.length + encoded.length;
656
- return total;
657
- }, 0);
658
- const typedArray = new Uint8Array(length);
659
- let currentIndex = 0;
660
- let index = 0;
661
- for (const source of input) {
662
- typedArray.set(encodedArray[index], currentIndex);
663
- currentIndex += encodedArray[index].length;
664
- typedArray.set(source, currentIndex);
665
- currentIndex += source.length;
666
- index += 1;
667
- }
668
- return typedArray;
789
+ const asBytes = (source) => {
790
+ if (source instanceof Uint8Array)
791
+ return source;
792
+ // Keep legacy ArrayLike coercion. Typed-array callers take the zero-copy path.
793
+ const bytes = new Uint8Array(source.length);
794
+ bytes.set(source);
795
+ return bytes;
669
796
  };
797
+ var index$3 = (input, prefix) => encodeFrames(input.map(asBytes));
670
798
 
671
- var index$2 = (typedArray, prefix) => {
672
- const set = [];
673
- const varintAndSub = (typedArray) => {
674
- const length = index$4.decode(typedArray);
675
- // remove length
676
- typedArray = typedArray.subarray(index$4.decode.bytes);
677
- // push value
678
- set.push(typedArray.subarray(0, length));
679
- // remove value
680
- typedArray = typedArray.subarray(length);
681
- if (typedArray.length !== 0)
682
- return varintAndSub(typedArray);
683
- return set;
684
- };
685
- return varintAndSub(typedArray);
686
- };
799
+ var index$2 = (typedArray, prefix) => decodeFrames(typedArray, 0, { canonical: false });
687
800
 
688
801
  const decode$2 = (multiWif, expectedVersion, expectedCodec) => {
689
802
  const decoded = base58$1.decode(multiWif);
@@ -741,176 +854,190 @@ var index$1$1 = {
741
854
  * * b = `7n` // equation param
742
855
  * * Gx, Gy are coordinates of Generator / base point
743
856
  */
857
+ const freeze = Object.freeze;
858
+ const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
859
+ const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
860
+ const Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
861
+ const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
744
862
  // Mirror noble-curves: Point.CURVE() returns shared params, but those params must stay frozen so
745
863
  // callers cannot mutate them out from under the arithmetic constants captured below.
746
- const secp256k1_CURVE = Object.freeze({
747
- p: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
748
- n: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,
864
+ const secp256k1_CURVE = freeze({
865
+ p: P,
866
+ n: N,
749
867
  h: 1n,
750
868
  a: 0n,
751
869
  b: 7n,
752
- Gx: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,
753
- Gy: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n,
870
+ Gx,
871
+ Gy,
754
872
  });
755
- const { p: P, n: N, Gx, Gy, b: _b } = secp256k1_CURVE;
756
873
  // 32-byte field / scalar width, and the SHA-256 / HMAC-DRBG output width used
757
874
  // by the RFC6979 paths here.
758
875
  const L = 32;
759
- const L2 = 64; // 64-byte compact signatures, and 64 hex chars for zero-padded 32-byte scalars
760
- const lengths = {
761
- publicKey: L + 1,
762
- publicKeyUncompressed: L2 + 1,
763
- // 48-byte keygen seed floor: 384 bits exceeds FIPS 186-5 Table A.2's
764
- // 352-bit recommendation for 256-bit prime curves.
765
- seed: L + L / 2,
766
- };
767
876
  // Helpers and Precomputes sections are reused between libraries
768
877
  // ## Helpers
769
878
  // ----------
770
- const err$1 = (message = '', E = Error) => {
771
- const e = new E(message);
772
- const { captureStackTrace } = Error;
773
- if (typeof captureStackTrace === 'function')
774
- captureStackTrace(e, err$1);
775
- throw e;
879
+ /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
880
+ const isBytes = (a) => {
881
+ // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.
882
+ // The fallback still requires a real ArrayBuffer view, so plain
883
+ // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and
884
+ // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
885
+ return (a instanceof Uint8Array ||
886
+ (ArrayBuffer.isView(a) &&
887
+ a.constructor.name === 'Uint8Array' &&
888
+ a.BYTES_PER_ELEMENT === 1));
776
889
  };
777
- // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases. The
778
- // fallback still requires a real ArrayBuffer view so plain JSON-deserialized `{ constructor: ... }`
779
- // spoofing is rejected, and `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.
780
- const isBytes = (a) => a instanceof Uint8Array ||
781
- (ArrayBuffer.isView(a) &&
782
- a.constructor.name === 'Uint8Array' &&
783
- a.BYTES_PER_ELEMENT === 1);
784
- /** Asserts something is Bytes. */
890
+ /** Asserts something is Uint8Array. */
785
891
  const abytes = (value, length, title = '') => {
892
+ // Success path first: this runs at the start of every update() / digestInto(), and the
893
+ // common `abytes(data)` form must not pay for length handling it does not use.
894
+ if (isBytes(value) && (length === undefined || value.length === length))
895
+ return value;
896
+ // Error path: recompute freely to build the exact message.
786
897
  const bytes = isBytes(value);
787
- const len = value?.length;
788
- const needsLen = length !== undefined;
789
- if (!bytes || (needsLen && len !== length)) {
790
- const prefix = title && `"${title}" `;
791
- const ofLen = needsLen ? ` of length ${length}` : '';
792
- const got = bytes ? `length=${len}` : `type=${typeof value}`;
793
- const msg = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;
794
- return bytes ? err$1(msg, RangeError) : err$1(msg, TypeError);
795
- }
796
- return value;
898
+ const ofLen = length !== undefined ? ` of length ${length}` : '';
899
+ const got = bytes ? `length=${value.length}` : `type=${typeof value}`;
900
+ const message = (title ? `"${title}" ` : '') + 'expected Uint8Array' + ofLen + ', got ' + got;
901
+ if (!bytes)
902
+ throw new TypeError(message);
903
+ throw new RangeError(message);
797
904
  };
798
- /** create Uint8Array */
799
- const u8n = (len) => new Uint8Array(len);
905
+ // Signing can retain the message across hash callbacks / awaits, and Schnorr hashes it more than
906
+ // once. Take one owned snapshot so caller mutation cannot change the signing transcript.
907
+ // cloneBytes expects an already-validated view; snapshotBytes validates, then copies.
908
+ const cloneBytes = (value) => Uint8Array.from(value);
909
+ const snapshotBytes = (value, title, length) => cloneBytes(abytes(value, length, title));
800
910
  // Callers keep values non-negative and within the requested width; padStart() won't truncate over-wide inputs.
801
911
  const padh = (n, pad) => n.toString(16).padStart(pad, '0');
802
- /** Render bytes as lowercase hex. */
803
- const bytesToHex = (b) => {
912
+ /** Convert byte array to hex string. */
913
+ const bytesToHex = (bytes) => {
804
914
  let hex = '';
805
- for (const e of abytes(b))
806
- hex += padh(e, 2);
915
+ for (const byte of abytes(bytes))
916
+ hex += padh(byte, 2);
807
917
  return hex;
808
918
  };
809
- const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
810
- // Strict ASCII nibble parser: non-ASCII hex lookalikes are rejected as undefined.
811
- // prettier-ignore
812
- const _ch = (ch) => ch >= C._0 && ch <= C._9 ? ch - C._0 // '2' => 50-48
813
- : ch >= C.A && ch <= C.F ? ch - (C.A - 10) // 'B' => 66-(65-10)
814
- : ch >= C.a && ch <= C.f ? ch - (C.a - 10) // 'b' => 98-(97-10)
815
- : undefined;
919
+ /** Convert hex string to byte array. */
816
920
  const hexToBytes = (hex) => {
817
- const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for type and parse failures.
921
+ const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for parse failures.
818
922
  if (typeof hex !== 'string')
819
- return err$1(e);
820
- const hl = hex.length;
821
- const al = hl / 2;
822
- if (hl % 2)
823
- return err$1(e);
824
- const array = u8n(al);
825
- for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
826
- // treat each char as ASCII
827
- const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
828
- const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
829
- if (n1 === undefined || n2 === undefined)
830
- return err$1(e);
831
- array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
923
+ throw new TypeError(e);
924
+ if (hex.length % 2 || !/^[\da-f]*$/i.test(hex))
925
+ throw new RangeError(e);
926
+ const array = new Uint8Array(hex.length / 2);
927
+ for (let ai = 0, hi = 0; ai < array.length; ai++, hi += 2) {
928
+ const n1 = hex.charCodeAt(hi);
929
+ const n2 = hex.charCodeAt(hi + 1);
930
+ // Regex guarantees ASCII. For 0..9/A..F/a..f, this maps char codes to 0..15.
931
+ array[ai] = ((n1 & 15) + (n1 >> 6) * 9) * 16 + (n2 & 15) + (n2 >> 6) * 9;
832
932
  }
833
933
  return array;
834
934
  };
835
935
  // WebCrypto is available in all modern environments
836
- const subtle = () => globalThis?.crypto?.subtle ?? err$1('crypto.subtle must be defined, consider polyfill');
837
- // prettier-ignore
838
- const concatBytes = (...arrs) => {
839
- let len = 0;
840
- for (const a of arrs)
841
- len += abytes(a).length; // validate every input and sum lengths before copying
842
- const r = u8n(len);
843
- let pad = 0; // walk through each array,
844
- for (const a of arrs)
845
- r.set(a, pad), pad += a.length; // ensure they have proper type
846
- return r;
936
+ const subtle = () => {
937
+ const s = globalThis?.crypto?.subtle;
938
+ if (s)
939
+ return s;
940
+ throw new Error('crypto.subtle must be defined, consider polyfill');
941
+ };
942
+ /** Copies several Uint8Arrays into one. */
943
+ const concatBytes = (...arrays) => {
944
+ let sum = 0;
945
+ for (const a of arrays)
946
+ sum += abytes(a).length;
947
+ const res = new Uint8Array(sum);
948
+ let pad = 0;
949
+ for (const a of arrays) {
950
+ res.set(a, pad);
951
+ pad += a.length;
952
+ }
953
+ return res;
847
954
  };
848
955
  /**
849
956
  * WebCrypto OS-level CSPRNG (random number generator).
850
957
  * Will throw when not available; large-request ceilings are delegated to getRandomValues().
851
958
  */
852
- const randomBytes = (len = L) => (globalThis?.crypto).getRandomValues(u8n(len));
959
+ const randomBytes = (len = L) => {
960
+ const c = globalThis?.crypto;
961
+ if (typeof c?.getRandomValues !== 'function')
962
+ throw new Error('crypto.getRandomValues must be defined, consider polyfill');
963
+ return c.getRandomValues(new Uint8Array(len));
964
+ };
853
965
  const big = BigInt;
854
966
  const arange = (n, min, max, msg = 'bad number: out of range') => {
855
967
  if (typeof n !== 'bigint')
856
- return err$1(msg, TypeError);
968
+ throw new TypeError(msg);
857
969
  if (min <= n && n < max)
858
970
  return n;
859
- return err$1(msg, RangeError);
971
+ throw new RangeError(msg);
860
972
  };
861
973
  /** Canonical modular reduction. Callers must provide a positive modulus. */
862
- const M = (a, b = P) => {
863
- const r = a % b;
864
- return r >= 0n ? r : b + r;
865
- };
974
+ const M = (a, b = P) => ((a %= b) >= 0n ? a : b + a);
866
975
  const modN = (a) => M(a, N);
867
- /** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
868
- // prettier-ignore
869
- const invert = (num, md) => {
870
- if (num === 0n || md <= 0n)
871
- err$1('no inverse n=' + num + ' mod=' + md);
872
- let a = M(num, md), b = md, x = 0n, u = 1n;
976
+ /** Modular inversion using extended euclidean GCD. Variable-time (non-CT). */
977
+ const invert = (number, modulo) => {
978
+ if (number === 0n)
979
+ throw new Error('invert: expected non-zero number');
980
+ // modulo = 1 is the zero ring: gcd(x, 1) = 1 makes the loop below "succeed" and return the
981
+ // useless inverse 0. Reject it.
982
+ if (modulo <= 1n)
983
+ throw new Error('invert: expected modulus > 1, got ' + modulo);
984
+ // This is variable-time: the loop count depends on `number`.
985
+ let a = M(number, modulo);
986
+ let b = modulo;
987
+ // Only the Bézout coefficient of `number` (x/u chain) is tracked; the coefficient of `modulo`
988
+ // never affects the output, so it is not computed.
989
+ // prettier-ignore
990
+ let x = 0n, u = 1n;
873
991
  while (a !== 0n) {
874
- const q = b / a, r = b % a;
992
+ const q = b / a;
993
+ const r = b - a * q;
875
994
  const m = x - u * q;
995
+ // prettier-ignore
876
996
  b = a, a = r, x = u, u = m;
877
997
  }
878
- return b === 1n ? M(x, md) : err$1('no inverse'); // b is gcd at this point
998
+ const gcd = b;
999
+ if (gcd !== 1n)
1000
+ throw new Error('invert: does not exist');
1001
+ return M(x, modulo);
879
1002
  };
880
- const callHash = (name) => {
1003
+ const _hash = (name) => {
881
1004
  // @ts-ignore
882
1005
  const fn = hashes[name];
883
1006
  if (typeof fn !== 'function')
884
- err$1('hashes.' + name + ' not set');
1007
+ throw new Error('hashes.' + name + ' not set');
885
1008
  return fn;
886
1009
  };
887
1010
  // All exported provider slots are caller-configurable and may be unset or return arbitrary values,
888
1011
  // so wrapper helpers must enforce the exact 32-byte digest contract instead of trusting providers.
889
- const gh = (name, a, b) => abytes(callHash(name)(a, b), L, 'digest');
890
- const gha = (name, a, b) => Promise.resolve(callHash(name)(a, b)).then((r) => abytes(r, L, 'digest'));
1012
+ const callHash = (name, a, b) => abytes(_hash(name)(a, b), L, 'digest');
1013
+ const callHashAsync = async (name, a, b) => abytes(await _hash(name)(a, b), L, 'digest');
891
1014
  // also rejects structurally similar Point values from other realms / bundled copies
892
- const apoint = (p) => (p instanceof Point ? p : err$1('Point expected'));
1015
+ const apoint = (p) => {
1016
+ if (p instanceof Point)
1017
+ return p;
1018
+ throw new TypeError('Point expected');
1019
+ };
893
1020
  // ## End of Helpers
894
1021
  // -----------------
1022
+ const E_BADPOINT = 'bad point: not on curve';
895
1023
  /**
896
1024
  * secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0,
897
1025
  * making it x³+b; callers validate x first.
898
1026
  */
899
- const koblitz = (x) => M(M(x * x) * x + _b);
1027
+ const koblitz = (x) => M(M(x * x) * x + 7n);
900
1028
  /** assert is element of field mod P (incl. 0 for projective infinity coordinates) */
901
1029
  const FpIsValid = (n) => arange(n, 0n, P);
902
1030
  /** assert is element of field mod P (excl. 0 where current callers need a non-zero coordinate) */
903
1031
  const FpIsValidNot0 = (n) => arange(n, 1n, P);
904
- /** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here */
1032
+ /** assert is element of field mod N (excl. 0), matching the shared BIP340 scalar-failure rule used here.
1033
+ * There is deliberately no FnIsValid: no caller accepts the scalar 0. */
905
1034
  const FnIsValidNot0 = (n) => arange(n, 1n, N);
906
1035
  // Shared parity primitive for BIP340 even-y checks and SEC 1 compressed prefixes.
907
1036
  const isEven = (y) => !(y & 1n);
908
- /** create Uint8Array of byte n */
909
- const u8of = (n) => Uint8Array.of(n);
910
1037
  /** SEC 1 compressed-prefix helper. Parity only: callers validate y before asking for the prefix byte. */
911
- const getPrefix = (y) => u8of(isEven(y) ? 0x02 : 0x03);
912
- /** lift_x from BIP340 returns the unique even square root for x³+7.
913
- * SEC 1 callers still flip it for the odd-prefix branch. */
1038
+ const getPrefix = (y) => Uint8Array.of(isEven(y) ? 0x02 : 0x03);
1039
+ /** lift_x from BIP340 returns the unique point with x and an even square root of x³+7.
1040
+ * SEC 1 callers still negate it for the odd-prefix branch. */
914
1041
  const lift_x = (x) => {
915
1042
  // Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1)
916
1043
  const c = koblitz(FpIsValidNot0(x));
@@ -926,8 +1053,8 @@ const lift_x = (x) => {
926
1053
  num = (num * num) % P; // Not constant-time.
927
1054
  }
928
1055
  if (M(r * r) !== c)
929
- err$1('sqrt invalid'); // check if result is valid
930
- return isEven(r) ? r : M(-r);
1056
+ throw new Error('sqrt invalid'); // check if result is valid
1057
+ return new Point(x, isEven(r) ? r : M(-r), 1n);
931
1058
  };
932
1059
  /**
933
1060
  * Point in 3d xyz projective coordinates. 3d takes less inversions than 2d.
@@ -951,7 +1078,7 @@ class Point {
951
1078
  this.X = FpIsValid(X);
952
1079
  this.Y = FpIsValidNot0(Y); // Y can't be 0 in Projective
953
1080
  this.Z = FpIsValid(Z);
954
- Object.freeze(this);
1081
+ freeze(this);
955
1082
  }
956
1083
  /** Returns the shared curve metadata object by reference.
957
1084
  * It is readonly only at type level, and mutating it won't retarget arithmetic,
@@ -967,30 +1094,31 @@ class Point {
967
1094
  /** Convert Uint8Array or hex string to Point. */
968
1095
  static fromBytes(bytes) {
969
1096
  abytes(bytes);
970
- const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
971
- let p = undefined;
972
1097
  const length = bytes.length;
973
1098
  const head = bytes[0];
974
- const tail = bytes.subarray(1);
975
- const x = sliceBytesNumBE(tail, 0, L);
1099
+ const x = sliceBytesNumBE(bytes, 1, 33);
976
1100
  // SEC 1 defines the rare infinity encoding 0x00, but SEC 1 public-key validation rejects
977
1101
  // infinity. We keep 0x00 rejected here because this parser is reused by verify(), ECDH,
978
1102
  // and public-key validation helpers, so strict handling applies to all callers by default.
979
1103
  // Local secp256k1 crosstests show OpenSSL raw point codecs accept 0x00 too.
980
- // Parse SEC 1 compressed/uncompressed encodings, then finish with assertValidity() before returning.
981
- if (length === comp && (head === 0x02 || head === 0x03)) {
982
- // Equation is y² == x³ + ax + b. We calculate y from x.
983
- // lift_x() returns the even root; SEC 1 0x03 still needs the odd root.
984
- let y = lift_x(x);
985
- if (head === 0x03)
986
- y = M(-y);
987
- p = new Point(x, y, 1n);
1104
+ // Parse and validate SEC 1 compressed/uncompressed encodings before returning.
1105
+ try {
1106
+ if (length === 33 && (head === 0x02 || head === 0x03)) {
1107
+ // Equation is y² == x³ + ax + b. We calculate y from x.
1108
+ // lift_x() returns the even point; SEC 1 0x03 still needs the odd point.
1109
+ const p = lift_x(x);
1110
+ return head === 0x03 ? p.negate() : p;
1111
+ }
1112
+ // Uncompressed 65-byte point, 0x04 prefix
1113
+ if (length === 65 && head === 0x04)
1114
+ return new Point(x, sliceBytesNumBE(bytes, 33, 65), 1n).assertValidity();
988
1115
  }
989
- // Uncompressed 65-byte point, 0x04 prefix
990
- if (length === uncomp && head === 0x04)
991
- p = new Point(x, sliceBytesNumBE(tail, L, L2), 1n);
992
- // Validate point
993
- return p ? p.assertValidity() : err$1('bad point: not on curve');
1116
+ catch (error) {
1117
+ // Out-of-range coordinates and non-residue x report the same error as wrong
1118
+ // prefixes / off-curve points, instead of generic range-check messages.
1119
+ throw new Error(E_BADPOINT);
1120
+ }
1121
+ throw new Error(E_BADPOINT);
994
1122
  }
995
1123
  static fromHex(hex) {
996
1124
  return Point.fromBytes(hexToBytes(hex));
@@ -1005,14 +1133,10 @@ class Point {
1005
1133
  equals(other) {
1006
1134
  const { X: X1, Y: Y1, Z: Z1 } = this;
1007
1135
  const { X: X2, Y: Y2, Z: Z2 } = apoint(other); // checks class equality
1008
- const X1Z2 = M(X1 * Z2);
1009
- const X2Z1 = M(X2 * Z1);
1010
- const Y1Z2 = M(Y1 * Z2);
1011
- const Y2Z1 = M(Y2 * Z1);
1012
- return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
1136
+ return M(X1 * Z2) === M(X2 * Z1) && M(Y1 * Z2) === M(Y2 * Z1);
1013
1137
  }
1014
1138
  is0() {
1015
- return this.equals(I);
1139
+ return this.Z === 0n;
1016
1140
  }
1017
1141
  /** Flip point over y coordinate. */
1018
1142
  negate() {
@@ -1032,7 +1156,7 @@ class Point {
1032
1156
  const { X: X1, Y: Y1, Z: Z1 } = this;
1033
1157
  const { X: X2, Y: Y2, Z: Z2 } = apoint(other);
1034
1158
  const a = 0n;
1035
- const b = _b;
1159
+ const b = 7n;
1036
1160
  let X3 = 0n, Y3 = 0n, Z3 = 0n;
1037
1161
  const b3 = M(b * 3n);
1038
1162
  let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
@@ -1096,13 +1220,18 @@ class Point {
1096
1220
  // init result point & fake point
1097
1221
  let p = I;
1098
1222
  let f = G;
1099
- for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
1223
+ let d = this;
1224
+ // Safe mode always runs 256 iterations so ladder length can't leak the scalar's
1225
+ // leading zero bits; unsafe mode stops at the top set bit for speed.
1226
+ for (let i = 0; safe ? i < 256 : n > 0n; i++) {
1100
1227
  // if bit is present, add to point
1101
1228
  // if not present, add to fake, for timing safety
1102
1229
  if (n & 1n)
1103
1230
  p = p.add(d);
1104
1231
  else if (safe)
1105
1232
  f = f.add(d);
1233
+ d = d.double();
1234
+ n >>= 1n;
1106
1235
  }
1107
1236
  return p;
1108
1237
  }
@@ -1113,14 +1242,14 @@ class Point {
1113
1242
  toAffine() {
1114
1243
  const { X: x, Y: y, Z: z } = this;
1115
1244
  // fast-paths for ZERO point OR Z=1
1116
- if (this.equals(I))
1245
+ if (z === 0n)
1117
1246
  return { x: 0n, y: 0n };
1118
1247
  if (z === 1n)
1119
1248
  return { x, y };
1120
1249
  const iz = invert(z, P);
1121
1250
  // (Z * Z^-1) must be 1, otherwise bad math
1122
1251
  if (M(z * iz) !== 1n)
1123
- err$1('inverse invalid');
1252
+ throw new Error('inverse invalid');
1124
1253
  // x = X*Z^-1; y = Y*Z^-1
1125
1254
  return { x: M(x * iz), y: M(y * iz) };
1126
1255
  }
@@ -1130,7 +1259,9 @@ class Point {
1130
1259
  FpIsValidNot0(x); // must be in range 1 <= x,y < P
1131
1260
  FpIsValidNot0(y);
1132
1261
  // y² == x³ + ax + b, equation sides must be equal
1133
- return M(y * y) === koblitz(x) ? this : err$1('bad point: not on curve');
1262
+ if (M(y * y) !== koblitz(x))
1263
+ throw new Error(E_BADPOINT);
1264
+ return this;
1134
1265
  }
1135
1266
  /** Converts point to 33/65-byte Uint8Array. */
1136
1267
  toBytes(isCompressed = true) {
@@ -1140,7 +1271,7 @@ class Point {
1140
1271
  const x32b = numTo32b(x);
1141
1272
  if (isCompressed)
1142
1273
  return concatBytes(getPrefix(y), x32b);
1143
- return concatBytes(u8of(0x04), x32b, numTo32b(y));
1274
+ return concatBytes(Uint8Array.of(0x04), x32b, numTo32b(y));
1144
1275
  }
1145
1276
  toHex(isCompressed) {
1146
1277
  return bytesToHex(this.toBytes(isCompressed));
@@ -1155,17 +1286,14 @@ Point.BASE = G;
1155
1286
  Point.ZERO = I;
1156
1287
  /** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
1157
1288
  const doubleScalarMulUns = (R, u1, u2) => {
1158
- return G.multiply(u1, false)
1159
- .add(R.multiply(u2, false))
1160
- .assertValidity();
1289
+ return G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity();
1161
1290
  };
1162
1291
  // Inherits byte validation from bytesToHex(); the || '0' fallback keeps empty input mapped to 0n.
1163
1292
  const bytesToNumBE = (b) => big('0x' + (bytesToHex(b) || '0'));
1164
1293
  // Callers provide monotone slice bounds; subarray() would otherwise clamp or reinterpret them silently.
1165
1294
  const sliceBytesNumBE = (b, from, to) => bytesToNumBE(b.subarray(from, to));
1166
- const B256 = 2n ** 256n; // secp256k1 is weierstrass curve. Equation is x³ + ax + b.
1167
- /** Generic 32-byte big-endian encoder. Must be 0 <= num < B256; call sites need not be field/scalar elements. */
1168
- const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, B256), L2));
1295
+ /** Generic 32-byte big-endian encoder. Must be 0 <= num < 2²⁵⁶; call sites need not be field/scalar elements. */
1296
+ const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, 2n ** 256n), L * 2)); // L*2 = 64 hex chars for a zero-padded 32-byte value
1169
1297
  /** Normalize private key to scalar (bigint). Verifies scalar is in range 1 <= d < N. */
1170
1298
  const secretKeyToScalar = (secretKey) => {
1171
1299
  const num = bytesToNumBE(abytes(secretKey, L, 'secret key'));
@@ -1196,12 +1324,11 @@ const isValidSecretKey = (secretKey) => {
1196
1324
  }
1197
1325
  };
1198
1326
  const isValidPublicKey = (publicKey, isCompressed) => {
1199
- const { publicKey: comp, publicKeyUncompressed } = lengths;
1200
1327
  try {
1201
1328
  const l = publicKey.length;
1202
- if (isCompressed === true && l !== comp)
1329
+ if (isCompressed === true && l !== 33)
1203
1330
  return false;
1204
- if (isCompressed === false && l !== publicKeyUncompressed)
1331
+ if (isCompressed === false && l !== 65)
1205
1332
  return false;
1206
1333
  return !!Point.fromBytes(publicKey);
1207
1334
  }
@@ -1229,12 +1356,11 @@ const _sha = 'SHA-256';
1229
1356
  const hashes = {
1230
1357
  hmacSha256Async: async (key, message) => {
1231
1358
  const s = subtle();
1232
- const name = 'HMAC';
1233
- const k = await s.importKey('raw', key, { name, hash: { name: _sha } }, false, ['sign']);
1234
- return u8n(await s.sign(name, k, message));
1359
+ const k = await s.importKey('raw', key, { name: 'HMAC', hash: _sha }, false, ['sign']);
1360
+ return new Uint8Array(await s.sign('HMAC', k, message));
1235
1361
  },
1236
1362
  hmacSha256: undefined,
1237
- sha256Async: async (msg) => u8n(await subtle().digest(_sha, msg)),
1363
+ sha256Async: async (msg) => new Uint8Array(await subtle().digest(_sha, msg)),
1238
1364
  sha256: undefined,
1239
1365
  };
1240
1366
  /**
@@ -1261,20 +1387,17 @@ const getSharedSecret = (secretKeyA, publicKeyB, isCompressed = true) => {
1261
1387
  // FIPS 186-5 Appendix A.4.1 style key generation reduces a wide random integer mod (n - 1) and adds 1.
1262
1388
  // The 48-byte minimum keeps the secp256k1 bias bound below the appendix's epsilon <= 2^-64 target.
1263
1389
  const randomSecretKey = (seed) => {
1264
- seed = seed === undefined ? randomBytes(lengths.seed) : seed;
1390
+ seed = seed === undefined ? randomBytes(48) : seed;
1265
1391
  abytes(seed);
1266
1392
  // Keep the public range text aligned with the enforced 48-byte FIPS floor.
1267
- if (seed.length < lengths.seed || seed.length > 1024)
1268
- return err$1('expected 48-1024b', RangeError);
1393
+ if (seed.length < 48 || seed.length > 1024)
1394
+ throw new RangeError('expected 48-1024b');
1269
1395
  const num = M(bytesToNumBE(seed), N - 1n);
1270
1396
  return numTo32b(num + 1n);
1271
1397
  };
1272
1398
  const createKeygen = (getPublicKey) => (seed) => {
1273
1399
  const secretKey = randomSecretKey(seed);
1274
- return {
1275
- secretKey,
1276
- publicKey: getPublicKey(secretKey),
1277
- };
1400
+ return { secretKey, publicKey: getPublicKey(secretKey) };
1278
1401
  };
1279
1402
  /**
1280
1403
  * Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves.
@@ -1285,17 +1408,17 @@ const createKeygen = (getPublicKey) => (seed) => {
1285
1408
  * const hex = etc.bytesToHex(new Uint8Array([1, 2, 3]));
1286
1409
  * ```
1287
1410
  */
1288
- const etc = /* @__PURE__ */ Object.freeze({
1411
+ const etc = /* @__PURE__ */ freeze({
1289
1412
  hexToBytes,
1290
1413
  bytesToHex,
1291
1414
  concatBytes,
1292
1415
  bytesToNumberBE: bytesToNumBE,
1293
1416
  numberToBytesBE: numTo32b,
1294
1417
  mod: M,
1295
- invert: invert, // math utilities; keep public alias type aligned with runtime
1418
+ invert,
1296
1419
  randomBytes,
1297
- secretKeyToScalar: secretKeyToScalar,
1298
- abytes: abytes,
1420
+ secretKeyToScalar,
1421
+ abytes,
1299
1422
  });
1300
1423
  /**
1301
1424
  * Curve-specific key utilities.
@@ -1307,26 +1430,23 @@ const etc = /* @__PURE__ */ Object.freeze({
1307
1430
  * const publicKey = secp.getPublicKey(secretKey);
1308
1431
  * ```
1309
1432
  */
1310
- const utils$2 = /* @__PURE__ */ Object.freeze({
1311
- isValidSecretKey: isValidSecretKey,
1312
- isValidPublicKey: isValidPublicKey,
1313
- randomSecretKey: randomSecretKey, // preserve the optional seeded call
1433
+ const utils$2 = /* @__PURE__ */ freeze({
1434
+ isValidSecretKey,
1435
+ isValidPublicKey,
1436
+ randomSecretKey,
1314
1437
  });
1315
1438
  // Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
1316
1439
  // https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
1317
1440
  // Internal BIP340 tag names are ASCII-only here, so charCodeAt() is enough; this is not a general UTF-8 encoder.
1318
1441
  const getTag = (tag) => Uint8Array.from('BIP0340/' + tag, (c) => c.charCodeAt(0));
1319
- const T_AUX = 'aux';
1320
- const T_NONCE = 'nonce';
1321
- const T_CHALLENGE = 'challenge';
1322
1442
  // Both SHA-256 provider slots are configurable, so tag hashing still goes through the checked
1323
1443
  // wrappers even though the built-in defaults are deterministic and the tag bytes are ASCII-only.
1324
1444
  const taggedHash = (tag, ...messages) => {
1325
- const tagH = gh('sha256', getTag(tag));
1326
- return gh('sha256', concatBytes(tagH, tagH, ...messages));
1445
+ const tagH = callHash('sha256', getTag(tag));
1446
+ return callHash('sha256', concatBytes(tagH, tagH, ...messages));
1327
1447
  };
1328
1448
  // Async twin of taggedHash with the same checked provider boundary.
1329
- const taggedHashAsync = (tag, ...messages) => gha('sha256Async', getTag(tag)).then((tagH) => gha('sha256Async', concatBytes(tagH, tagH, ...messages)));
1449
+ const taggedHashAsync = (tag, ...messages) => callHashAsync('sha256Async', getTag(tag)).then((tagH) => callHashAsync('sha256Async', concatBytes(tagH, tagH, ...messages)));
1330
1450
  // BIP340 PubKey(sk) = bytes(d'⋅G), where bytes(P) is bytes(x(P)); signing also normalizes
1331
1451
  // d to the equivalent scalar whose point has even y so the x-only public key stays canonical.
1332
1452
  const extpubSchnorr = (priv) => {
@@ -1338,8 +1458,8 @@ const extpubSchnorr = (priv) => {
1338
1458
  return { d, px };
1339
1459
  };
1340
1460
  const bytesModN = (bytes) => modN(bytesToNumBE(bytes));
1341
- const challenge = (...args) => bytesModN(taggedHash(T_CHALLENGE, ...args));
1342
- const challengeAsync = async (...args) => bytesModN(await taggedHashAsync(T_CHALLENGE, ...args));
1461
+ const challenge = (...args) => bytesModN(taggedHash('challenge', ...args));
1462
+ const challengeAsync = async (...args) => bytesModN(await taggedHashAsync('challenge', ...args));
1343
1463
  /** Schnorr public key is just `x` coordinate of Point as per BIP340. */
1344
1464
  const pubSchnorr = (secretKey) => {
1345
1465
  return extpubSchnorr(secretKey).px; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
@@ -1347,13 +1467,14 @@ const pubSchnorr = (secretKey) => {
1347
1467
  const keygenSchnorr = /* @__PURE__ */ createKeygen(pubSchnorr);
1348
1468
  // Common preparation fn for both sync and async signing
1349
1469
  const prepSigSchnorr = (message, secretKey, auxRand) => {
1470
+ const m = snapshotBytes(message, 'message');
1350
1471
  const { px, d } = extpubSchnorr(secretKey);
1351
- return { m: abytes(message), px, d, a: abytes(auxRand, L) };
1472
+ return { m, px, d, a: abytes(auxRand, L) };
1352
1473
  };
1353
1474
  const extractK = (rand) => {
1354
1475
  const k_ = bytesModN(rand); // Let k' = int(rand) mod n
1355
1476
  if (k_ === 0n)
1356
- err$1('sign failed: k is zero'); // Fail if k' = 0.
1477
+ throw new Error('sign failed: k is zero'); // Fail if k' = 0.
1357
1478
  const { px, d } = extpubSchnorr(numTo32b(k_)); // Let R = k'⋅G.
1358
1479
  return { rx: px, k: d };
1359
1480
  };
@@ -1369,73 +1490,78 @@ const E_INVSIG = 'invalid signature produced';
1369
1490
  */
1370
1491
  const signSchnorr = (message, secretKey, auxRand = randomBytes(L)) => {
1371
1492
  const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
1372
- const aux = taggedHash(T_AUX, a);
1373
1493
  // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
1374
- const t = numTo32b(d ^ bytesToNumBE(aux));
1494
+ const t = numTo32b(d ^ bytesToNumBE(taggedHash('aux', a)));
1375
1495
  // Let rand = hash/nonce(t || bytes(P) || m)
1376
- const rand = taggedHash(T_NONCE, t, px, m);
1377
- const { rx, k } = extractK(rand);
1496
+ const { rx, k } = extractK(taggedHash('nonce', t, px, m));
1378
1497
  // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
1379
- const e = challenge(rx, px, m);
1380
- const sig = createSigSchnorr(k, rx, e, d);
1498
+ const sig = createSigSchnorr(k, rx, challenge(rx, px, m), d);
1381
1499
  // If Verify(bytes(P), m, sig) (see below) returns failure, abort
1382
1500
  if (!verifySchnorr(sig, m, px))
1383
- err$1(E_INVSIG);
1501
+ throw new Error(E_INVSIG);
1384
1502
  return sig;
1385
1503
  };
1386
1504
  const signSchnorrAsync = async (message, secretKey, auxRand = randomBytes(L)) => {
1387
1505
  const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand);
1388
- const aux = await taggedHashAsync(T_AUX, a);
1389
1506
  // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
1390
- const t = numTo32b(d ^ bytesToNumBE(aux));
1507
+ const t = numTo32b(d ^ bytesToNumBE(await taggedHashAsync('aux', a)));
1391
1508
  // Let rand = hash/nonce(t || bytes(P) || m)
1392
- const rand = await taggedHashAsync(T_NONCE, t, px, m);
1393
- const { rx, k } = extractK(rand);
1509
+ const { rx, k } = extractK(await taggedHashAsync('nonce', t, px, m));
1394
1510
  // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
1395
- const e = await challengeAsync(rx, px, m);
1396
- const sig = createSigSchnorr(k, rx, e, d);
1511
+ const sig = createSigSchnorr(k, rx, await challengeAsync(rx, px, m), d);
1397
1512
  // If Verify(bytes(P), m, sig) (see below) returns failure, abort
1398
1513
  if (!(await verifySchnorrAsync(sig, m, px)))
1399
- err$1(E_INVSIG);
1514
+ throw new Error(E_INVSIG);
1400
1515
  return sig;
1401
1516
  };
1402
1517
  const callSyncAsyncFn = (res, later) => {
1403
1518
  return res instanceof Promise ? res.then(later) : later(res);
1404
1519
  };
1405
1520
  const _verifSchnorr = (signature, message, publicKey, challengeFn) => {
1406
- const sig = abytes(signature, L2, 'signature');
1521
+ const sig = abytes(signature, 64, 'signature');
1407
1522
  const msg = abytes(message, undefined, 'message');
1408
1523
  const pub = abytes(publicKey, L, 'publicKey');
1524
+ let P_;
1525
+ let r;
1526
+ let s;
1527
+ let chalInput; // challenge preimage bytes(r) || bytes(P) || m
1409
1528
  try {
1410
1529
  // lift_x from BIP340. Convert 32-byte x coordinate to elliptic curve point.
1411
1530
  // Fail if x ≥ p. Let c = x³ + 7 mod p.
1412
1531
  const x = bytesToNumBE(pub);
1413
- const y = lift_x(x); // lift_x already returns the unique even root required by BIP340.
1414
- const P_ = new Point(x, y, 1n).assertValidity();
1415
- const px = numTo32b(P_.toAffine().x);
1532
+ P_ = lift_x(x); // lift_x returns the validated even-y point required by BIP340.
1416
1533
  // P = lift_x(int(pk)); fail if that fails
1417
- const r = sliceBytesNumBE(sig, 0, L); // Let r = int(sig[0:32]); fail if r ≥ p.
1418
- arange(r, 1n, P);
1419
- const s = sliceBytesNumBE(sig, L, L2); // Let s = int(sig[32:64]); fail if s ≥ n.
1534
+ r = FpIsValidNot0(sliceBytesNumBE(sig, 0, L)); // Let r = int(sig[0:32]); fail if r ≥ p.
1535
+ // Let s = int(sig[32:64]); fail if s ≥ n.
1420
1536
  // Stricter than BIP-340/libsecp256k1, which only reject s >= n. Honest signing reaches
1421
1537
  // s = 0 only with negligible probability (k + e*d ≡ 0 mod n), so treat zero-s inputs as
1422
1538
  // crafted edge cases and fail closed instead of carrying that extra verification surface.
1423
- arange(s, 1n, N);
1424
- const i = concatBytes(numTo32b(r), px, msg);
1425
- // int(challenge(bytes(r)||bytes(P)||m))%n
1426
- return callSyncAsyncFn(challengeFn(i), (e) => {
1427
- const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
1428
- if (!isEven(y) || x !== r)
1429
- return false; // -eP == (n-e)P
1430
- return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
1431
- });
1539
+ s = FnIsValidNot0(sliceBytesNumBE(sig, L, 64));
1540
+ // lift_x validated x < P, so the 32-byte input encoding equals bytes(P.x) exactly.
1541
+ chalInput = concatBytes(numTo32b(r), pub, msg);
1432
1542
  }
1433
1543
  catch (error) {
1434
1544
  return false;
1435
1545
  }
1546
+ // Hash-provider errors (unset hashes.sha256, missing crypto.subtle, bad provider digests)
1547
+ // stay outside the catch above: backend misconfiguration is not an "invalid signature"
1548
+ // result, so it throws / rejects instead of becoming false.
1549
+ // int(challenge(bytes(r)||bytes(P)||m))%n
1550
+ return callSyncAsyncFn(challengeFn(chalInput), (e) => {
1551
+ try {
1552
+ const { x, y } = doubleScalarMulUns(P_, s, modN(-e)).toAffine(); // R = s⋅G - e⋅P
1553
+ if (!isEven(y) || x !== r)
1554
+ return false; // -eP == (n-e)P
1555
+ return true; // Fail if is_infinite(R) / not has_even_y(R) / x(R) ≠ r.
1556
+ }
1557
+ catch (error) {
1558
+ return false; // is_infinite(R) throws inside doubleScalarMulUns
1559
+ }
1560
+ });
1436
1561
  };
1437
- /** Verifies Schnorr signature. Sync wrapper returns false for post-validation failures
1438
- * after the initial byte checks. */
1562
+ /** Verifies Schnorr signature. Invalid points, scalars and failed curve checks return false;
1563
+ * hash-backend misconfiguration (e.g. unset hashes.sha256) throws instead, since it is a
1564
+ * runtime/backend error, not an "invalid signature" result. */
1439
1565
  const verifySchnorr = (s, m, p) => _verifSchnorr(s, m, p, challenge);
1440
1566
  /** Async Schnorr verification. Curve/encoding failures after the initial byte checks still
1441
1567
  * become false, but async backend failures reject the promise. Missing crypto.subtle is a
@@ -1457,7 +1583,7 @@ const verifySchnorrAsync = async (s, m, p) => _verifSchnorr(s, m, p, challengeAs
1457
1583
  * const isValid = secp.schnorr.verify(sig, message, publicKey);
1458
1584
  * ```
1459
1585
  */
1460
- const schnorr = /* @__PURE__ */ Object.freeze({
1586
+ const schnorr = /* @__PURE__ */ freeze({
1461
1587
  keygen: keygenSchnorr,
1462
1588
  getPublicKey: pubSchnorr,
1463
1589
  sign: signSchnorr,
@@ -1467,18 +1593,14 @@ const schnorr = /* @__PURE__ */ Object.freeze({
1467
1593
  });
1468
1594
  // ## Precomputes
1469
1595
  // --------------
1470
- const W = 8; // W is window size
1471
- const scalarBits = 256;
1472
- const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8, NOT 32 - see wNAF loop
1473
- const pwindowSize = 2 ** (W - 1); // 128 for W=8
1474
1596
  const precompute = () => {
1475
1597
  const points = [];
1476
1598
  let p = G;
1477
1599
  let b = p;
1478
- for (let w = 0; w < pwindows; w++) {
1600
+ for (let w = 0; w < 33; w++) {
1479
1601
  b = p;
1480
1602
  points.push(b);
1481
- for (let i = 1; i < pwindowSize; i++) {
1603
+ for (let i = 1; i < 128; i++) {
1482
1604
  b = b.add(p);
1483
1605
  points.push(b);
1484
1606
  } // i=1, bc we skip 0
@@ -1507,37 +1629,32 @@ const wNAF = (n) => {
1507
1629
  const comp = Gpows || (Gpows = precompute());
1508
1630
  let p = I;
1509
1631
  let f = G; // f must be G, or could become I in the end
1510
- const pow_2_w = 2 ** W; // 256 for W=8
1511
- const maxNum = pow_2_w; // 256 for W=8
1512
- const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111
1513
- const shiftBy = big(W); // 8 for W=8
1514
- for (let w = 0; w < pwindows; w++) {
1515
- let wbits = Number(n & mask); // extract W bits.
1516
- n >>= shiftBy; // shift number by W bits.
1632
+ for (let w = 0; w < 33; w++) {
1633
+ let wbits = Number(n & 255n); // extract W=8 bits.
1634
+ n >>= 8n; // shift number by W=8 bits.
1517
1635
  // We use negative indexes to reduce size of precomputed table by 2x.
1518
1636
  // Instead of needing precomputes 0..256, we only calculate them for 0..128.
1519
1637
  // If an index > 128 is found, we do (256-index) - where 256 is next window.
1520
1638
  // Naive: index +127 => 127, +224 => 224
1521
1639
  // Optimized: index +127 => 127, +224 => 256-32
1522
- if (wbits > pwindowSize) {
1523
- wbits -= maxNum;
1640
+ if (wbits > 128) {
1641
+ wbits -= 256;
1524
1642
  n += 1n;
1525
1643
  }
1526
- const off = w * pwindowSize;
1527
- const offF = off; // offsets, evaluate both
1644
+ const off = w * 128;
1528
1645
  const offP = off + Math.abs(wbits) - 1;
1529
- const isEven = w % 2 !== 0; // conditions, evaluate both
1646
+ const isOddW = w % 2 !== 0; // conditions, evaluate both; alternates fake-add sign per window
1530
1647
  const isNeg = wbits < 0;
1531
1648
  if (wbits === 0) {
1532
- // off == I: can't add it. Adding random offF instead.
1533
- f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point
1649
+ // off == I: can't add it. Add the first point in this window to the fake accumulator.
1650
+ f = f.add(ctneg(isOddW, comp[off])); // bits are 0: add garbage to fake point
1534
1651
  }
1535
1652
  else {
1536
1653
  p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
1537
1654
  }
1538
1655
  }
1539
1656
  if (n !== 0n)
1540
- err$1('invalid wnaf');
1657
+ throw new Error('invalid wnaf');
1541
1658
  return { p, f }; // return both real and fake points for JIT/leakage-shape symmetry
1542
1659
  };
1543
1660
 
@@ -2457,10 +2574,10 @@ class HdNode {
2457
2574
  return (async () => (await this.identifier).subarray(0, 4))();
2458
2575
  }
2459
2576
  async hash160(data) {
2460
- const hash = await createHash(data, 'SHA-256');
2577
+ const hash = await createHash(data, "SHA-256");
2461
2578
  return (await createRIPEMD160())
2462
2579
  .update(new Uint8Array(hash))
2463
- .digest('binary');
2580
+ .digest("binary");
2464
2581
  }
2465
2582
  get isNeutered() {
2466
2583
  return this.#privateKey === undefined;
@@ -2470,24 +2587,24 @@ class HdNode {
2470
2587
  }
2471
2588
  fromPrivateKey(privateKey, chainCode, network) {
2472
2589
  if (!privateKeyVerify(privateKey))
2473
- throw new TypeError('Private key not in range [1, n)');
2590
+ throw new TypeError("Private key not in range [1, n)");
2474
2591
  const publicKey = publicKeyCreate(privateKey, true);
2475
2592
  return new HdNode(privateKey, publicKey, publicKey.slice(1), network);
2476
2593
  }
2477
2594
  fromPublicKey(publicKey, chainCode, network) {
2478
2595
  // verify the X coordinate is a point on the curve
2479
2596
  if (!publicKeyVerify(publicKey))
2480
- throw new TypeError('Point is not on the curve');
2597
+ throw new TypeError("Point is not on the curve");
2481
2598
  return new HdNode(undefined, publicKey, chainCode, network);
2482
2599
  }
2483
2600
  async fromSeed(seed, network) {
2484
2601
  if (seed.length < 16)
2485
- throw new TypeError('Seed should be at least 128 bits');
2602
+ throw new TypeError("Seed should be at least 128 bits");
2486
2603
  if (seed.length > 64)
2487
- throw new TypeError('Seed should be at most 512 bits');
2488
- let hash = (await createHMAC(createSHA512(), new TextEncoder().encode('Bitcoin seed')))
2604
+ throw new TypeError("Seed should be at most 512 bits");
2605
+ let hash = (await createHMAC(createSHA512(), new TextEncoder().encode("Bitcoin seed")))
2489
2606
  .update(seed)
2490
- .digest('binary');
2607
+ .digest("binary");
2491
2608
  const privateKey = hash.subarray(0, 32);
2492
2609
  const chainCode = hash.subarray(32);
2493
2610
  return this.fromPrivateKey(privateKey, chainCode, network);
@@ -2505,7 +2622,7 @@ class HdNode {
2505
2622
  this.#chainCode,
2506
2623
  ];
2507
2624
  if (!this.isNeutered) {
2508
- set.push(new TextEncoder().encode('0'));
2625
+ set.push(new TextEncoder().encode("0"));
2509
2626
  set.push(new Uint8Array(this.privateKey));
2510
2627
  }
2511
2628
  else {
@@ -2515,7 +2632,7 @@ class HdNode {
2515
2632
  }
2516
2633
  toWIF() {
2517
2634
  if (!this.#privateKey)
2518
- throw new TypeError('Missing private key');
2635
+ throw new TypeError("Missing private key");
2519
2636
  return wif.encode(this.#network.wif, this.#privateKey, true);
2520
2637
  }
2521
2638
  // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions
@@ -2525,10 +2642,10 @@ class HdNode {
2525
2642
  // Hardened child
2526
2643
  if (isHardened) {
2527
2644
  if (this.isNeutered)
2528
- throw new TypeError('Missing private key for hardened child key');
2645
+ throw new TypeError("Missing private key for hardened child key");
2529
2646
  // data = 0x00 || ser256(kpar) || ser32(index)
2530
2647
  data = index$3([
2531
- new TextEncoder().encode('0'),
2648
+ new TextEncoder().encode("0"),
2532
2649
  this.privateKey,
2533
2650
  new TextEncoder().encode(index.toString()),
2534
2651
  ]);
@@ -2541,7 +2658,7 @@ class HdNode {
2541
2658
  }
2542
2659
  const hash = (await createHMAC(createSHA512(), this.#chainCode))
2543
2660
  .update(data)
2544
- .digest('binary');
2661
+ .digest("binary");
2545
2662
  const privateKey = hash.subarray(0, 32);
2546
2663
  const chainCode = hash.subarray(32);
2547
2664
  // if parse256(privateKey) >= n, proceed with the next value for i
@@ -2568,10 +2685,10 @@ class HdNode {
2568
2685
  return this.derive(index + HIGHEST_BIT);
2569
2686
  }
2570
2687
  async derivePath(path) {
2571
- let splitPath = path.split('/');
2572
- if (splitPath[0] === 'm') {
2688
+ let splitPath = path.split("/");
2689
+ if (splitPath[0] === "m") {
2573
2690
  if (this.#parentFingerprint)
2574
- throw new TypeError('Expected master, got child');
2691
+ throw new TypeError("Expected master, got child");
2575
2692
  splitPath = splitPath.slice(1);
2576
2693
  }
2577
2694
  let prevHd = this;
@@ -2603,16 +2720,16 @@ class HdNode {
2603
2720
  const k = privateKey ? 0 : result[5];
2604
2721
  const chainCode = result[4];
2605
2722
  if (version !== network.bip32.private && version !== network.bip32.public)
2606
- throw new TypeError('Invalid network version');
2723
+ throw new TypeError("Invalid network version");
2607
2724
  if (depth === 0) {
2608
2725
  if (parentFingerprint !== 0)
2609
- throw new TypeError('Invalid parent fingerprint');
2726
+ throw new TypeError("Invalid parent fingerprint");
2610
2727
  }
2611
2728
  if (depth === 0 && index !== 0)
2612
- throw new TypeError('Invalid index');
2729
+ throw new TypeError("Invalid index");
2613
2730
  if (version === network.bip32.private) {
2614
2731
  if (k !== 0x00)
2615
- throw new TypeError('Invalid private key');
2732
+ throw new TypeError("Invalid private key");
2616
2733
  return new HdNode(privateKey, undefined, chainCode, network, depth, index, parentFingerprint);
2617
2734
  }
2618
2735
  return new HdNode(undefined,
@@ -4868,16 +4985,16 @@ const ALPHABET$2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
4868
4985
  const ALPHABET_HEX = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
4869
4986
  const base32 = base(ALPHABET$2);
4870
4987
  const base32Hex = base(ALPHABET_HEX);
4871
- const decode$6 = base32.decode;
4988
+ const decode$5 = base32.decode;
4872
4989
  const decodeHex = base32Hex.decode;
4873
- const encode$5 = base32.encode;
4990
+ const encode$4 = base32.encode;
4874
4991
  const encodeHex = base32Hex.encode;
4875
4992
  const isBase32 = (string, hex = false) => {
4876
4993
  try {
4877
4994
  if (hex)
4878
4995
  decodeHex(string);
4879
4996
  else
4880
- decode$6(string);
4997
+ decode$5(string);
4881
4998
  return true;
4882
4999
  }
4883
5000
  catch (e) {
@@ -4887,9 +5004,9 @@ const isBase32 = (string, hex = false) => {
4887
5004
  const isBase32Hex = (string) => {
4888
5005
  return isBase32(string, true);
4889
5006
  };
4890
- var index$5 = {
4891
- encode: encode$5,
4892
- decode: decode$6,
5007
+ var index$4 = {
5008
+ encode: encode$4,
5009
+ decode: decode$5,
4893
5010
  encodeHex,
4894
5011
  decodeHex,
4895
5012
  isBase32,
@@ -4964,11 +5081,8 @@ class MultiSignature {
4964
5081
  signature = signature || this.signature;
4965
5082
  if (!signature)
4966
5083
  throw ReferenceError("signature undefined");
4967
- this.#multiSignature = typedArrayConcat([
4968
- index$4.encode(this.version),
4969
- index$4.encode(this.multiCodec),
4970
- signature,
4971
- ]);
5084
+ const writer = new BinaryWriter(varUintLength(this.version) + varUintLength(this.multiCodec) + signature.length);
5085
+ this.#multiSignature = writer.writeVarUint(this.version).writeVarUint(this.multiCodec).writeBytes(signature).finish();
4972
5086
  return this.multiSignature;
4973
5087
  }
4974
5088
  /**
@@ -4981,12 +5095,11 @@ class MultiSignature {
4981
5095
  this.#multiSignature = multiSignature;
4982
5096
  if (!this.multiSignature)
4983
5097
  throw ReferenceError("multiSignature undefined");
4984
- let buffer = this.multiSignature;
4985
- const version = index$4.decode(buffer);
4986
- buffer = buffer.subarray(index$4.decode.bytes);
4987
- const codec = index$4.decode(buffer);
4988
- buffer = buffer.subarray(index$4.decode.bytes);
4989
- const signature = buffer.subarray(0, buffer.length);
5098
+ const reader = new BinaryReader(this.multiSignature);
5099
+ // Keep legacy padded-header acceptance; do not change signature wire rules.
5100
+ const version = reader.readVarUint(false);
5101
+ const codec = reader.readVarUint(false);
5102
+ const signature = reader.readBytes(reader.remaining);
4990
5103
  console.log("[decode] extracted signature length:", signature.length);
4991
5104
  console.log("[decode] extracted signature (hex):", Buffer.from(signature).toString("hex"));
4992
5105
  if (version !== this.version)
@@ -5004,7 +5117,7 @@ class MultiSignature {
5004
5117
  return this.multiSignature.toString();
5005
5118
  }
5006
5119
  fromString(string) {
5007
- return this.decode(new Uint8Array(string.split(",")));
5120
+ return this.decode(new Uint8Array(string.split(",").map(Number)));
5008
5121
  }
5009
5122
  toBs58() {
5010
5123
  return base58$1.encode(this.multiSignature);
@@ -5013,16 +5126,16 @@ class MultiSignature {
5013
5126
  return this.decode(base58$1.decode(string));
5014
5127
  }
5015
5128
  toBs32() {
5016
- return index$5.encode(this.multiSignature);
5129
+ return index$4.encode(this.multiSignature);
5017
5130
  }
5018
5131
  fromBs32(string) {
5019
- return this.decode(index$5.decode(string));
5132
+ return this.decode(index$4.decode(string));
5020
5133
  }
5021
5134
  toBs32Hex() {
5022
- return index$5.encodeHex(this.multiSignature);
5135
+ return index$4.encodeHex(this.multiSignature);
5023
5136
  }
5024
5137
  fromBs32Hex(string) {
5025
- return this.decode(index$5.decodeHex(string));
5138
+ return this.decode(index$4.decodeHex(string));
5026
5139
  }
5027
5140
  toBs58Hex() {
5028
5141
  return base58$1.encodeHex(this.multiSignature);
@@ -5079,17 +5192,26 @@ class MultiHDNode extends HDWallet {
5079
5192
  get id() {
5080
5193
  return base58check.encode(index$3([
5081
5194
  new TextEncoder().encode(this.version.toString()),
5082
- this.account(0).hdnode.neutered.publicKey
5195
+ this.account(0).hdnode.neutered.publicKey,
5083
5196
  ]));
5084
5197
  }
5085
5198
  get multiWIF() {
5086
5199
  return this.toMultiWif();
5087
5200
  }
5088
5201
  async fromId(id) {
5089
- let buffer = (await base58check.decode(id)).data;
5090
- index$4.decode(buffer);
5091
- buffer = buffer.slice(index$4.decode.bytes);
5092
- this.fromPublicKey(buffer, null, this.networkName);
5202
+ const buffer = (await base58check.decode(id)).data;
5203
+ const reader = new BinaryReader(buffer);
5204
+ // IDs contain two framed fields: textual version and compressed public key.
5205
+ // Keep legacy padded length prefixes readable without changing generated IDs.
5206
+ const version = reader.readFrame(false);
5207
+ const publicKey = reader.readFrame(false);
5208
+ const expectedVersion = new TextEncoder().encode(this.version.toString());
5209
+ if (reader.remaining !== 0 ||
5210
+ version.length !== expectedVersion.length ||
5211
+ !version.every((byte, index) => byte === expectedVersion[index]) ||
5212
+ publicKey.length !== 33)
5213
+ throw new Error("invalid wallet ID");
5214
+ await this.fromPublicKey(publicKey, null, this.networkName);
5093
5215
  }
5094
5216
  async import(password, encrypted) {
5095
5217
  const { prefix, data } = await base58check.decode(encrypted);
@@ -5118,22 +5240,20 @@ class MultiHDNode extends HDWallet {
5118
5240
  return c.testnet;
5119
5241
  else
5120
5242
  return p;
5121
- }, networks['leofcoin']);
5243
+ }, networks["leofcoin"]);
5122
5244
  if (version !== this.network.version)
5123
- throw new Error('invalid version');
5245
+ throw new Error("invalid version");
5124
5246
  return this.fromPrivateKey(privateKey, undefined, this.network);
5125
5247
  }
5126
5248
  toMultiWif() {
5127
5249
  return index$1$1.encode(this.network.version, this.network.multiCodec, this.privateKey);
5128
5250
  }
5129
5251
  sign(hash) {
5130
- return new MultiSignature(this.version, this.network.multiCodec)
5131
- .sign(hash, this.privateKey);
5252
+ return new MultiSignature(this.version, this.network.multiCodec).sign(hash, this.privateKey);
5132
5253
  }
5133
5254
  verify(multiSignature, hash) {
5134
5255
  const publicKey = this.publicKey?.length === 33 ? this.publicKey.slice(1) : this.publicKey;
5135
- return new MultiSignature(this.version, this.network.multiCodec)
5136
- .verify(multiSignature, hash, publicKey);
5256
+ return new MultiSignature(this.version, this.network.multiCodec).verify(multiSignature, hash, publicKey);
5137
5257
  }
5138
5258
  }
5139
5259
 
@@ -8390,7 +8510,7 @@ class Identity {
8390
8510
  this.selectedAccount = new TextDecoder().decode(selected);
8391
8511
  }
8392
8512
  else {
8393
- const importee = await import(/* webpackChunkName: "generate-account" */ './index-Bxr5Iztg-WbFIosyf.js');
8513
+ const importee = await import(/* webpackChunkName: "generate-account" */ './index-dTQVgkpf-CuHJWO2l.js');
8394
8514
  const { identity, accounts } = await importee.default(password, this.network);
8395
8515
  if (fresh) {
8396
8516
  await globalThis.accountStore.clear();
@@ -15682,7 +15802,7 @@ const toHex = (bytes) => bytes.reduce((string, byte) => string + byte.toString(1
15682
15802
  const fromArrayLike = (array) => Uint8Array.from(array);
15683
15803
  const toBase58 = (uint8Array) => base58$1.encode(uint8Array);
15684
15804
  const fromBase58 = (string) => base58$1.decode(string);
15685
- const toBase32 = (uint8Array) => index$5.encode(uint8Array);
15805
+ const toBase32 = (uint8Array) => index$4.encode(uint8Array);
15686
15806
  var index$1 = {
15687
15807
  toString: toString$1};
15688
15808
 
@@ -15782,13 +15902,13 @@ class BasicInterface {
15782
15902
  this.fromBs58(value);
15783
15903
  }
15784
15904
  catch {
15785
- if (index$5.isBase32(value))
15905
+ if (index$4.isBase32(value))
15786
15906
  this.fromBs32(value);
15787
15907
  else
15788
15908
  throw new Error('Invalid base58/base32 input');
15789
15909
  }
15790
15910
  }
15791
- else if (index$5.isBase32(value))
15911
+ else if (index$4.isBase32(value))
15792
15912
  this.fromBs32(value);
15793
15913
  else
15794
15914
  this.fromString(value);
@@ -15856,13 +15976,13 @@ class BasicInterface {
15856
15976
  return isHex(string);
15857
15977
  }
15858
15978
  isBase32(string) {
15859
- return index$5.isBase32(string);
15979
+ return index$4.isBase32(string);
15860
15980
  }
15861
15981
  isBase58(string) {
15862
15982
  return base58$1.isBase58(string);
15863
15983
  }
15864
15984
  fromBs32(encoded) {
15865
- return this.decode(index$5.decode(encoded));
15985
+ return this.decode(index$4.decode(encoded));
15866
15986
  }
15867
15987
  fromBs58(encoded) {
15868
15988
  return this.decode(fromBase58(encoded));
@@ -16190,7 +16310,7 @@ let Codec$1 = class Codec extends BasicInterface {
16190
16310
  super();
16191
16311
  if (buffer) {
16192
16312
  if (buffer instanceof Uint8Array) {
16193
- const codec = index$4.decode(buffer);
16313
+ const codec = new BinaryReader(buffer).readVarUint(false);
16194
16314
  const name = this.getCodecName(codec);
16195
16315
  if (name) {
16196
16316
  this.name = name;
@@ -16202,11 +16322,12 @@ let Codec$1 = class Codec extends BasicInterface {
16202
16322
  }
16203
16323
  }
16204
16324
  else if (buffer instanceof ArrayBuffer) {
16205
- const codec = index$4.decode(buffer);
16325
+ const view = new Uint8Array(buffer);
16326
+ const codec = new BinaryReader(view).readVarUint(false);
16206
16327
  const name = this.getCodecName(codec);
16207
16328
  if (name) {
16208
16329
  this.name = name;
16209
- this.decode(buffer);
16330
+ this.decode(view);
16210
16331
  }
16211
16332
  else {
16212
16333
  this.encode(Number(new TextDecoder().decode(new Uint8Array(buffer))));
@@ -16231,7 +16352,7 @@ let Codec$1 = class Codec extends BasicInterface {
16231
16352
  }
16232
16353
  }
16233
16354
  fromEncoded(encoded) {
16234
- const codec = index$4.decode(encoded);
16355
+ const codec = new BinaryReader(encoded).readVarUint(false);
16235
16356
  const name = this.getCodecName(codec);
16236
16357
  this.name = name;
16237
16358
  this.encoded = encoded;
@@ -16250,7 +16371,7 @@ let Codec$1 = class Codec extends BasicInterface {
16250
16371
  this.name = this.getCodecName(codec);
16251
16372
  this.hashAlg = this.getHashAlg(this.name);
16252
16373
  this.codec = this.getCodec(this.name);
16253
- this.codecBuffer = index$4.encode(this.codec);
16374
+ this.codecBuffer = new BinaryWriter(varUintLength(this.codec)).writeVarUint(this.codec).finish();
16254
16375
  this.decoded = {
16255
16376
  name: this.name,
16256
16377
  hashAlg: this.hashAlg,
@@ -16263,7 +16384,7 @@ let Codec$1 = class Codec extends BasicInterface {
16263
16384
  this.name = name;
16264
16385
  this.codec = codec;
16265
16386
  this.hashAlg = this.getHashAlg(name);
16266
- this.codecBuffer = index$4.encode(this.codec);
16387
+ this.codecBuffer = new BinaryWriter(varUintLength(this.codec)).writeVarUint(this.codec).finish();
16267
16388
  this.decoded = {
16268
16389
  name: this.name,
16269
16390
  hashAlg: this.hashAlg,
@@ -16273,13 +16394,13 @@ let Codec$1 = class Codec extends BasicInterface {
16273
16394
  }
16274
16395
  decode(encoded) {
16275
16396
  encoded = encoded || this.encoded;
16276
- const codec = index$4.decode(encoded);
16397
+ const codec = new BinaryReader(encoded).readVarUint(false);
16277
16398
  this.fromCodec(codec);
16278
16399
  return this.decoded;
16279
16400
  }
16280
16401
  encode(codec) {
16281
16402
  codec = codec || this.codec;
16282
- this.encoded = index$4.encode(codec);
16403
+ this.encoded = new BinaryWriter(varUintLength(codec)).writeVarUint(codec).finish();
16283
16404
  return this.encoded;
16284
16405
  }
16285
16406
  };
@@ -16308,10 +16429,37 @@ const digestBuffer = async (hashVariant, buffer) => {
16308
16429
  const digest = hasher.digest('binary');
16309
16430
  return digest instanceof Uint8Array ? digest : new Uint8Array(digest);
16310
16431
  };
16432
+ const hashHeader = (buffer) => {
16433
+ if (!(buffer instanceof Uint8Array))
16434
+ throw new TypeError('CodecHash only supports Uint8Array input');
16435
+ const reader = new BinaryReader(buffer);
16436
+ const codec = reader.readVarUint();
16437
+ const name = utils.getCodecName(codec);
16438
+ if (!name)
16439
+ throw new Error('unknown hash codec');
16440
+ const size = reader.readVarUint();
16441
+ if (size !== reader.remaining)
16442
+ throw new Error('hash length inconsistent');
16443
+ return { name, size, offset: reader.position };
16444
+ };
16445
+ const hasHashHeader = (buffer) => {
16446
+ const reader = new BinaryReader(buffer);
16447
+ try {
16448
+ // Recognition is deliberately separate from strict parsing: a padded known
16449
+ // codec must reach decode() and fail, not silently become raw content.
16450
+ return Boolean(utils.getCodecName(reader.readVarUint(false)));
16451
+ }
16452
+ catch (error) {
16453
+ if (!(error instanceof RangeError))
16454
+ throw error;
16455
+ return false;
16456
+ }
16457
+ };
16311
16458
  class CodecHash extends BasicInterface {
16312
16459
  codec;
16313
16460
  discoCodec;
16314
16461
  size;
16462
+ digest;
16315
16463
  codecs;
16316
16464
  constructor(options = { name: 'disco-hash', codecs: {} }) {
16317
16465
  super();
@@ -16319,21 +16467,14 @@ class CodecHash extends BasicInterface {
16319
16467
  this.name = options.name;
16320
16468
  else
16321
16469
  this.name = 'disco-hash';
16322
- if (options?.codecs)
16323
- this.codecs = options.codecs;
16470
+ this.codecs = options?.codecs ?? {};
16324
16471
  }
16325
16472
  async init(uint8Array) {
16326
16473
  if (uint8Array) {
16327
16474
  if (!(uint8Array instanceof Uint8Array)) {
16328
16475
  throw new Error('CodecHash only supports Uint8Array input');
16329
16476
  }
16330
- // For large buffers, only check first bytes for codec prefix
16331
- // Codec prefixes are small (varint encoded), so 100 bytes is more than enough
16332
- const checkBuffer = uint8Array.length > 100 ? uint8Array.subarray(0, 100) : uint8Array;
16333
- this.discoCodec = new Codec$1(checkBuffer);
16334
- const name = this.discoCodec.name;
16335
- if (name) {
16336
- this.name = name;
16477
+ if (hasHashHeader(uint8Array)) {
16337
16478
  this.decode(uint8Array);
16338
16479
  }
16339
16480
  else {
@@ -16342,15 +16483,19 @@ class CodecHash extends BasicInterface {
16342
16483
  }
16343
16484
  return this;
16344
16485
  }
16486
+ #clearEncodingCache() {
16487
+ for (const key of ['_bs32', '_hex', '_string'])
16488
+ delete this[key];
16489
+ }
16345
16490
  get prefix() {
16346
16491
  const length = this.length;
16347
16492
  const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length);
16348
- uint8Array.set(length);
16349
- uint8Array.set(this.discoCodec.codecBuffer, length.length);
16493
+ uint8Array.set(this.discoCodec.codecBuffer);
16494
+ uint8Array.set(length, this.discoCodec.codecBuffer.length);
16350
16495
  return uint8Array;
16351
16496
  }
16352
16497
  get length() {
16353
- return index$4.encode(this.size);
16498
+ return new BinaryWriter(varUintLength(this.size)).writeVarUint(this.size).finish();
16354
16499
  }
16355
16500
  get buffer() {
16356
16501
  return this.encoded;
@@ -16377,6 +16522,8 @@ class CodecHash extends BasicInterface {
16377
16522
  const uint8Array = new Uint8Array(this.digest.length + this.prefix.length);
16378
16523
  uint8Array.set(this.prefix);
16379
16524
  uint8Array.set(this.digest, this.prefix.length);
16525
+ this.#clearEncodingCache();
16526
+ this.decoded = undefined;
16380
16527
  this.encoded = uint8Array;
16381
16528
  return this.encoded;
16382
16529
  }
@@ -16384,35 +16531,30 @@ class CodecHash extends BasicInterface {
16384
16531
  if (!(buffer instanceof Uint8Array)) {
16385
16532
  throw new Error('CodecHash only supports Uint8Array input');
16386
16533
  }
16387
- const codec = index$4.decode(buffer);
16388
- if (this.codecs[codec]) {
16389
- this.decode(buffer);
16390
- }
16391
- else {
16392
- await this.encode(buffer);
16393
- }
16534
+ await this.init(buffer);
16394
16535
  }
16395
16536
  decode(buffer) {
16537
+ buffer = buffer ?? this.encoded;
16538
+ // Validate completely before changing this instance, including cached encodings.
16539
+ const header = hashHeader(buffer);
16540
+ const codec = new Codec$1(header.name);
16541
+ const digest = new Uint8Array(buffer.subarray(header.offset));
16542
+ this.#clearEncodingCache();
16396
16543
  this.encoded = buffer;
16397
- const codec = index$4.decode(buffer);
16398
- this.discoCodec = new Codec$1(codec, this.codecs);
16399
- // TODO: validate codec
16400
- buffer = buffer.slice(index$4.decode.bytes);
16401
- this.size = index$4.decode(buffer);
16402
- this.digest = buffer.slice(index$4.decode.bytes);
16403
- if (this.digest.length !== this.size) {
16404
- throw new Error(`hash length inconsistent: 0x${this.encoded.toString()}`);
16405
- }
16406
- // const discoCodec = new Codec(codec, this.codecs)
16407
- this.name = this.discoCodec.name;
16408
- this.size = this.digest.length;
16409
- return {
16544
+ this.discoCodec = codec;
16545
+ this.codec = codec.codecBuffer;
16546
+ this.name = header.name;
16547
+ this.size = header.size;
16548
+ this.digest = digest;
16549
+ const decoded = {
16410
16550
  codec: this.codec,
16411
16551
  name: this.name,
16412
16552
  size: this.size,
16413
16553
  length: this.length,
16414
16554
  digest: this.digest
16415
16555
  };
16556
+ this.decoded = decoded;
16557
+ return decoded;
16416
16558
  }
16417
16559
  }
16418
16560
 
@@ -16525,13 +16667,14 @@ let FormatInterface$1 = class FormatInterface extends BasicInterface {
16525
16667
  !Object.prototype.hasOwnProperty.call(this.decoded, 'hash')) {
16526
16668
  // @ts-ignore
16527
16669
  const hash = new CodecHash({ name: this.name });
16528
- return hash.init(this.contentBytes);
16670
+ // These are explicitly raw message bytes, even if they resemble a hash header.
16671
+ return hash.encode(this.contentBytes).then(() => hash);
16529
16672
  }
16530
16673
  // Fallback: must re-encode without hash property
16531
16674
  const decoded = this.beforeHashing({ ...this.decoded });
16532
16675
  // @ts-ignore
16533
16676
  const hash = new CodecHash({ name: this.name });
16534
- return hash.init(decoded);
16677
+ return hash.encode(this.protoEncode(decoded)).then(() => hash);
16535
16678
  }
16536
16679
  /**
16537
16680
  * @return {peernetHash}
@@ -16984,6 +17127,196 @@ let LeofcoinStorage$1 = class LeofcoinStorage {
16984
17127
  }
16985
17128
  };
16986
17129
 
17130
+ /** A resumable download. Completed chunks remain cached on the transfer instance. */
17131
+ class FileTransfer {
17132
+ hash;
17133
+ state = 'idle';
17134
+ result;
17135
+ error;
17136
+ #fetch;
17137
+ #decode;
17138
+ #verify;
17139
+ #pin;
17140
+ #verifyManifest;
17141
+ #concurrency;
17142
+ #stopped = false;
17143
+ #listeners = new Set();
17144
+ #chunks = new Map();
17145
+ #resumeWaiters = [];
17146
+ #resolve;
17147
+ #reject;
17148
+ #totalChunks = 0;
17149
+ #totalBytes = 0;
17150
+ #started = false;
17151
+ constructor(options) {
17152
+ this.hash = options.hash;
17153
+ this.#fetch = options.fetch;
17154
+ this.#decode = options.decode;
17155
+ this.#verify = options.verify;
17156
+ this.#pin = options.pin;
17157
+ this.#verifyManifest = options.verifyManifest !== false;
17158
+ this.#concurrency = options.concurrency ?? 4;
17159
+ if (!Number.isSafeInteger(this.#concurrency) || this.#concurrency <= 0)
17160
+ throw new TypeError('concurrency must be a positive integer');
17161
+ this.result = new Promise((resolve, reject) => {
17162
+ this.#resolve = resolve;
17163
+ this.#reject = reject;
17164
+ });
17165
+ }
17166
+ get progress() {
17167
+ let transferredBytes = 0;
17168
+ for (const chunk of this.#chunks.values())
17169
+ transferredBytes += chunk.length;
17170
+ return {
17171
+ hash: this.hash,
17172
+ state: this.state,
17173
+ completedChunks: this.#chunks.size,
17174
+ totalChunks: this.#totalChunks,
17175
+ transferredBytes,
17176
+ totalBytes: this.#totalBytes
17177
+ };
17178
+ }
17179
+ onProgress(listener) {
17180
+ this.#listeners.add(listener);
17181
+ listener(this.progress);
17182
+ return () => this.#listeners.delete(listener);
17183
+ }
17184
+ start() {
17185
+ if (!this.#started) {
17186
+ this.#started = true;
17187
+ this.state = 'running';
17188
+ this.#emit();
17189
+ void this.#run();
17190
+ }
17191
+ return this;
17192
+ }
17193
+ pause() {
17194
+ if (this.state === 'running') {
17195
+ this.state = 'paused';
17196
+ this.#emit();
17197
+ }
17198
+ }
17199
+ resume() {
17200
+ if (this.state !== 'paused')
17201
+ return;
17202
+ this.state = 'running';
17203
+ const waiters = this.#resumeWaiters.splice(0);
17204
+ for (const resolve of waiters)
17205
+ resolve();
17206
+ this.#emit();
17207
+ }
17208
+ cancel() {
17209
+ if (this.state === 'completed' || this.state === 'failed' || this.state === 'cancelled')
17210
+ return;
17211
+ this.state = 'cancelled';
17212
+ this.#stopped = true;
17213
+ const error = new Error(`Transfer ${this.hash} was cancelled`);
17214
+ this.error = error;
17215
+ const waiters = this.#resumeWaiters.splice(0);
17216
+ for (const resolve of waiters)
17217
+ resolve();
17218
+ this.#reject(error);
17219
+ this.#emit();
17220
+ }
17221
+ async #waitUntilRunning() {
17222
+ if (this.state === 'paused')
17223
+ await new Promise((resolve) => this.#resumeWaiters.push(resolve));
17224
+ if (this.state === 'cancelled')
17225
+ throw this.error;
17226
+ }
17227
+ async #run() {
17228
+ try {
17229
+ const manifestData = await this.#fetch(this.hash);
17230
+ if (!manifestData)
17231
+ throw new Error(`Unable to download ${this.hash}`);
17232
+ if (this.#verifyManifest && !(await this.#verify(manifestData, this.hash)))
17233
+ throw new Error(`Manifest hash mismatch for ${this.hash}`);
17234
+ const manifest = await this.#decode(manifestData);
17235
+ if (this.#pin)
17236
+ await this.#pin(this.hash, manifestData);
17237
+ if (!manifest.chunked) {
17238
+ if (manifest.links?.length)
17239
+ throw new Error(`${this.hash} is a directory`);
17240
+ const content = manifest.content || new Uint8Array();
17241
+ this.#totalChunks = 1;
17242
+ this.#totalBytes = content.length;
17243
+ this.#chunks.set(0, content);
17244
+ this.state = 'completed';
17245
+ this.#resolve(content);
17246
+ this.#emit();
17247
+ return;
17248
+ }
17249
+ const links = manifest.links || [];
17250
+ this.#totalChunks = links.length;
17251
+ this.#totalBytes =
17252
+ Number(manifest.size) || links.reduce((sum, link) => sum + (Number(link.size) || 0), 0);
17253
+ this.#emit();
17254
+ let nextIndex = 0;
17255
+ const worker = async () => {
17256
+ while (!this.#stopped) {
17257
+ await this.#waitUntilRunning();
17258
+ const index = nextIndex++;
17259
+ if (index >= links.length)
17260
+ return;
17261
+ if (this.#chunks.has(index))
17262
+ continue;
17263
+ const encoded = await this.#fetch(links[index].hash, index);
17264
+ if (this.#stopped)
17265
+ return;
17266
+ if (!encoded)
17267
+ throw new Error(`Missing chunk ${index + 1}/${links.length}`);
17268
+ if (!(await this.#verify(encoded, links[index].hash)))
17269
+ throw new Error(`Hash mismatch for chunk ${index + 1}`);
17270
+ const chunkNode = await this.#decode(encoded);
17271
+ const content = chunkNode.content || new Uint8Array();
17272
+ this.#chunks.set(index, content);
17273
+ if (this.#pin)
17274
+ await this.#pin(links[index].hash, encoded);
17275
+ this.#emit();
17276
+ }
17277
+ };
17278
+ await Promise.all(Array.from({ length: Math.min(this.#concurrency, links.length) }, () => worker()));
17279
+ const output = new Uint8Array(this.#totalBytes);
17280
+ let offset = 0;
17281
+ for (let index = 0; index < this.#totalChunks; index++) {
17282
+ const chunk = this.#chunks.get(index);
17283
+ if (!chunk)
17284
+ throw new Error(`Missing downloaded chunk ${index + 1}`);
17285
+ output.set(chunk, offset);
17286
+ offset += chunk.length;
17287
+ }
17288
+ this.state = 'completed';
17289
+ this.#resolve(offset === output.length ? output : output.slice(0, offset));
17290
+ this.#emit();
17291
+ }
17292
+ catch (error) {
17293
+ if (this.state === 'cancelled')
17294
+ return;
17295
+ this.state = 'failed';
17296
+ this.#stopped = true;
17297
+ this.error = error;
17298
+ this.#reject(error);
17299
+ this.#emit();
17300
+ }
17301
+ }
17302
+ #emit() {
17303
+ const progress = this.progress;
17304
+ for (const listener of this.#listeners)
17305
+ listener(progress);
17306
+ }
17307
+ }
17308
+
17309
+ const DEFAULT_CHUNK_SIZE = 256 * 1024;
17310
+ const DEFAULT_BLOCK_CHUNK_SIZE = 256 * 1024;
17311
+ const DEFAULT_BLOCK_CHUNK_THRESHOLD = 1024 * 1024;
17312
+ const DEFAULT_TRANSFER_CONCURRENCY = 4;
17313
+ const adaptiveChunkSize = (size, minimum = DEFAULT_CHUNK_SIZE) => {
17314
+ if (size >= 256 * 1024 * 1024)
17315
+ return Math.max(minimum, 4 * 1024 * 1024);
17316
+ if (size >= 32 * 1024 * 1024)
17317
+ return Math.max(minimum, 1024 * 1024);
17318
+ return minimum;
17319
+ };
16987
17320
  globalThis.LeofcoinStorage = LeofcoinStorage$1;
16988
17321
  globalThis.leofcoin = globalThis.leofcoin || {};
16989
17322
  globalThis.pubsub = globalThis.pubsub || new LittlePubSub();
@@ -17021,6 +17354,9 @@ class Peernet {
17021
17354
  _peerHandler;
17022
17355
  protos;
17023
17356
  version;
17357
+ blockChunkSize;
17358
+ blockChunkThreshold;
17359
+ transferConcurrency;
17024
17360
  #peerAttempts = {};
17025
17361
  _inMemoryBroadcasts;
17026
17362
  /**
@@ -17047,6 +17383,15 @@ class Peernet {
17047
17383
  this.stars = options.stars;
17048
17384
  this.transport = options.transport;
17049
17385
  this.version = options.version;
17386
+ this.blockChunkSize = options.blockChunkSize ?? DEFAULT_BLOCK_CHUNK_SIZE;
17387
+ this.blockChunkThreshold = options.blockChunkThreshold ?? DEFAULT_BLOCK_CHUNK_THRESHOLD;
17388
+ this.transferConcurrency = options.transferConcurrency ?? DEFAULT_TRANSFER_CONCURRENCY;
17389
+ if (!Number.isSafeInteger(this.blockChunkSize) || this.blockChunkSize <= 0)
17390
+ throw new TypeError('blockChunkSize must be a positive integer');
17391
+ if (!Number.isSafeInteger(this.blockChunkThreshold) || this.blockChunkThreshold < 0)
17392
+ throw new TypeError('blockChunkThreshold must be a non-negative integer');
17393
+ if (!Number.isSafeInteger(this.transferConcurrency) || this.transferConcurrency <= 0)
17394
+ throw new TypeError('transferConcurrency must be a positive integer');
17050
17395
  const parts = this.network.split(':');
17051
17396
  this.networkVersion = options.networkVersion || parts.length > 1 ? parts[1] : 'mainnet';
17052
17397
  if (!options.storePrefix)
@@ -17139,7 +17484,7 @@ class Peernet {
17139
17484
  await getAddress();
17140
17485
  this.storePrefix = options.storePrefix;
17141
17486
  this.root = options.root;
17142
- const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile } = await import(/* webpackChunkName: "messages" */ './messages-DH0tgNM9-C2CtyLsf.js');
17487
+ const { RequestMessage, ResponseMessage, PeerMessage, PeerMessageResponse, PeernetMessage, DHTMessage, DHTMessageResponse, DataMessage, DataMessageResponse, PsMessage, ChatMessage, PeernetFile } = await import(/* webpackChunkName: "messages" */ './messages-Dw86z0zq-DrPw_nlZ.js');
17143
17488
  /**
17144
17489
  * proto Object containing protos
17145
17490
  * @type {Object}
@@ -17186,7 +17531,7 @@ class Peernet {
17186
17531
  if (this.#starting || this.#started)
17187
17532
  return;
17188
17533
  this.#starting = true;
17189
- const importee = await import('./client-DDC8C510-3uaB70KL.js');
17534
+ const importee = await import('./client-CAC7RVMf-Dmz_2A_5.js');
17190
17535
  /**
17191
17536
  * @access public
17192
17537
  * @type {PeernetClient}
@@ -17275,12 +17620,34 @@ class Peernet {
17275
17620
 
17276
17621
  * @returns {Promise<string>} The hash that can be shared for direct download
17277
17622
  */
17278
- async broadcast(path, { content, links }) {
17623
+ async broadcast(path, { content, links, chunkSize }) {
17624
+ chunkSize = chunkSize ?? (content ? adaptiveChunkSize(content.length) : DEFAULT_CHUNK_SIZE);
17625
+ if (!Number.isSafeInteger(chunkSize) || chunkSize <= 0)
17626
+ throw new TypeError('chunkSize must be a positive integer');
17279
17627
  let protoInput;
17280
- if (content)
17281
- protoInput = { path, content };
17628
+ if (content && content.length > chunkSize) {
17629
+ const chunkLinks = [];
17630
+ for (let offset = 0, index = 0; offset < content.length; offset += chunkSize, index++) {
17631
+ const chunkNode = await new globalThis.peernet.protos['peernet-file']({
17632
+ path: `${path}.part-${index}`,
17633
+ content: content.slice(offset, Math.min(offset + chunkSize, content.length))
17634
+ });
17635
+ const chunkHash = await chunkNode.hash();
17636
+ const chunkEncoded = await chunkNode.encoded;
17637
+ if (!this._inMemoryBroadcasts)
17638
+ this._inMemoryBroadcasts = new Map();
17639
+ this._inMemoryBroadcasts.set(chunkHash, chunkEncoded);
17640
+ await shareStore.put(chunkHash, chunkEncoded);
17641
+ chunkLinks.push({ hash: chunkHash, path: String(index), size: chunkNode.decoded.content.length });
17642
+ }
17643
+ protoInput = { path, links: chunkLinks, size: content.length, chunkSize, chunked: true };
17644
+ }
17645
+ else if (content)
17646
+ protoInput = { path, content, size: content.length };
17282
17647
  else if (links)
17283
17648
  protoInput = { path, links };
17649
+ else
17650
+ throw new TypeError('broadcast requires content or links');
17284
17651
  const protoNode = await new globalThis.peernet.protos['peernet-file'](protoInput);
17285
17652
  const hash = await protoNode.hash();
17286
17653
  const encoded = await protoNode.encoded;
@@ -17292,6 +17659,46 @@ class Peernet {
17292
17659
  await this.publish('broadcast', { hash, from: this.id });
17293
17660
  return hash;
17294
17661
  }
17662
+ /** Create and immediately start a resumable, integrity-checked file download. */
17663
+ download(hash, options = {}) {
17664
+ const FileProto = globalThis.peernet.protos['peernet-file'];
17665
+ const transfer = new FileTransfer({
17666
+ hash,
17667
+ fetch: async (wantedHash, index) => {
17668
+ if (wantedHash !== hash) {
17669
+ const providers = Object.values(this.dht.providersFor(hash) || {});
17670
+ for (const provider of providers)
17671
+ this.dht.addProvider(provider, wantedHash);
17672
+ }
17673
+ const store = await this.whichStore([...this.stores], wantedHash);
17674
+ if (store && (await store.has(wantedHash)))
17675
+ return store.get(wantedHash);
17676
+ const result = await this.requestData(wantedHash, undefined, { providerIndex: index });
17677
+ return result;
17678
+ },
17679
+ decode: async (encoded) => {
17680
+ const node = await new FileProto(encoded);
17681
+ await node.decode();
17682
+ if (node.decoded?.chunked) {
17683
+ const providers = Object.values(this.dht.providersFor(hash) || {});
17684
+ for (const link of node.decoded.links || []) {
17685
+ for (const provider of providers)
17686
+ this.dht.addProvider(provider, link.hash);
17687
+ }
17688
+ }
17689
+ return node.decoded;
17690
+ },
17691
+ concurrency: this.transferConcurrency,
17692
+ verify: async (encoded, expectedHash) => {
17693
+ const node = await new FileProto(encoded);
17694
+ return (await node.hash()) === expectedHash;
17695
+ },
17696
+ pin: options.pin ? (chunkHash, encoded) => dataStore.put(chunkHash, encoded) : undefined
17697
+ });
17698
+ if (options.autoStart !== false)
17699
+ transfer.start();
17700
+ return transfer;
17701
+ }
17295
17702
  async handleData(peer, id, proto) {
17296
17703
  let { hash, store } = proto.decoded;
17297
17704
  let data;
@@ -17302,15 +17709,9 @@ class Peernet {
17302
17709
  if (typeof hash === 'function') {
17303
17710
  resolvedHash = await hash();
17304
17711
  }
17305
- // Decode the stored proto to extract the content or links
17306
- const FileProto = globalThis.peernet.protos['peernet-file'];
17307
- const fileProto = await new FileProto(data);
17308
- await fileProto.decode();
17309
- const { content, links } = fileProto.decoded;
17310
- console.log(links);
17311
17712
  data = await new globalThis.peernet.protos['peernet-data-response']({
17312
17713
  hash: resolvedHash,
17313
- data: links || content
17714
+ data
17314
17715
  });
17315
17716
  const node = await this.prepareMessage(data);
17316
17717
  await this.sendMessage(peer, id, node.encoded);
@@ -17466,19 +17867,93 @@ class Peernet {
17466
17867
  get block() {
17467
17868
  return {
17468
17869
  get: async (hash) => {
17469
- const data = await blockStore.has(hash);
17470
- if (data)
17471
- return blockStore.get(hash);
17472
- return this.requestData(hash, 'block');
17870
+ return this.#createBlockTransfer(hash).result;
17473
17871
  },
17474
17872
  put: async (hash, data) => {
17475
17873
  if (await blockStore.has(hash))
17476
17874
  return;
17875
+ if (data.length > this.blockChunkThreshold)
17876
+ return this.#putChunkedBlock(hash, data);
17477
17877
  return await blockStore.put(hash, data);
17478
17878
  },
17479
- has: async (hash) => await blockStore.has(hash)
17879
+ has: async (hash) => await blockStore.has(hash),
17880
+ download: (hash, options = {}) => this.#createBlockTransfer(hash, options)
17480
17881
  };
17481
17882
  }
17883
+ async #putChunkedBlock(hash, data) {
17884
+ const links = [];
17885
+ const FileProto = globalThis.peernet.protos['peernet-file'];
17886
+ const chunkSize = adaptiveChunkSize(data.length, this.blockChunkSize);
17887
+ for (let offset = 0, index = 0; offset < data.length; offset += chunkSize, index++) {
17888
+ const content = data.slice(offset, Math.min(offset + chunkSize, data.length));
17889
+ const chunk = new FileProto({ path: `block-${hash}.part-${index}`, content });
17890
+ const chunkHash = await chunk.hash();
17891
+ await blockStore.put(chunkHash, chunk.encoded);
17892
+ links.push({ hash: chunkHash, path: String(index).padStart(12, '0'), size: content.length });
17893
+ }
17894
+ const manifest = new FileProto({
17895
+ path: `block-${hash}`,
17896
+ links,
17897
+ size: data.length,
17898
+ chunkSize,
17899
+ chunked: true,
17900
+ kind: 'block',
17901
+ blockHash: hash
17902
+ });
17903
+ return blockStore.put(hash, manifest.encoded);
17904
+ }
17905
+ #createBlockTransfer(hash, options = {}) {
17906
+ const FileProto = globalThis.peernet.protos['peernet-file'];
17907
+ const fetch = async (wantedHash, index) => {
17908
+ if (wantedHash !== hash) {
17909
+ const providers = Object.values(this.dht.providersFor(hash) || {});
17910
+ for (const provider of providers)
17911
+ this.dht.addProvider(provider, wantedHash);
17912
+ }
17913
+ if (await blockStore.has(wantedHash))
17914
+ return blockStore.get(wantedHash);
17915
+ const result = await this.requestData(wantedHash, 'block', { providerIndex: index });
17916
+ return result;
17917
+ };
17918
+ const transfer = new FileTransfer({
17919
+ hash,
17920
+ fetch,
17921
+ decode: async (encoded) => {
17922
+ let node;
17923
+ try {
17924
+ node = new FileProto(encoded);
17925
+ await node.decode();
17926
+ }
17927
+ catch {
17928
+ // A normal block is intentionally not a peernet-file envelope.
17929
+ return { content: encoded };
17930
+ }
17931
+ if (node.decoded?.kind === 'block') {
17932
+ if (node.decoded.blockHash !== hash)
17933
+ throw new Error(`Block manifest hash mismatch for ${hash}`);
17934
+ const providers = Object.values(this.dht.providersFor(hash) || {});
17935
+ for (const link of node.decoded.links || []) {
17936
+ for (const provider of providers)
17937
+ this.dht.addProvider(provider, link.hash);
17938
+ }
17939
+ return node.decoded;
17940
+ }
17941
+ if (node.decoded?.path?.startsWith(`block-${hash}.part-`))
17942
+ return node.decoded;
17943
+ return { content: encoded };
17944
+ },
17945
+ verify: async (encoded, expectedHash) => {
17946
+ const node = new FileProto(encoded);
17947
+ return (await node.hash()) === expectedHash;
17948
+ },
17949
+ verifyManifest: false,
17950
+ concurrency: this.transferConcurrency,
17951
+ pin: (wantedHash, encoded) => blockStore.put(wantedHash, encoded)
17952
+ });
17953
+ if (options.autoStart !== false)
17954
+ transfer.start();
17955
+ return transfer;
17956
+ }
17482
17957
  get transaction() {
17483
17958
  return {
17484
17959
  get: async (hash) => {
@@ -17495,14 +17970,17 @@ class Peernet {
17495
17970
  has: async (hash) => await transactionStore.has(hash)
17496
17971
  };
17497
17972
  }
17498
- async requestData(hash, store) {
17973
+ async requestData(hash, store, options = {}) {
17499
17974
  try {
17500
17975
  const providers = await this.providersFor(hash);
17501
17976
  if (!providers || (providers && Object.keys(providers).length === 0))
17502
17977
  throw nothingFoundError(hash);
17503
17978
  debug(`found ${Object.keys(providers).length} provider(s) for ${hash}`);
17504
17979
  // get closest peer on earth
17505
- let closestPeer = await this.dht.closestPeer(Object.values(providers));
17980
+ const providerValues = Object.values(providers);
17981
+ let closestPeer = options.providerIndex === undefined
17982
+ ? await this.dht.closestPeer(providerValues)
17983
+ : providerValues[options.providerIndex % providerValues.length];
17506
17984
  // fallback to first provider if no closest peer found
17507
17985
  if (!closestPeer || !closestPeer.id)
17508
17986
  closestPeer = Object.values(providers)[0];
@@ -17514,7 +17992,7 @@ class Peernet {
17514
17992
  const peer = this.connections[id];
17515
17993
  if (!peer || !peer?.connected) {
17516
17994
  this.dht.removeProvider(id, hash);
17517
- return this.requestData(hash, store?.name || store);
17995
+ return this.requestData(hash, store?.name || store, options);
17518
17996
  }
17519
17997
  let data = await new globalThis.peernet.protos['peernet-data']({
17520
17998
  hash,
@@ -17557,7 +18035,7 @@ class Peernet {
17557
18035
  if (this.#peerAttempts[id] === undefined)
17558
18036
  this.#peerAttempts[id] = 0;
17559
18037
  this.#peerAttempts[id]++;
17560
- return this.requestData(hash, store?.name || store);
18038
+ return this.requestData(hash, store?.name || store, options);
17561
18039
  }
17562
18040
  // this.put(hash, proto.decoded.data)
17563
18041
  }
@@ -17734,18 +18212,8 @@ class Peernet {
17734
18212
  return paths;
17735
18213
  }
17736
18214
  async cat(hash, options) {
17737
- let data;
17738
- const has = await dataStore.has(hash);
17739
- data = has ? await dataStore.get(hash) : await this.requestData(hash, 'data');
17740
- if (!data)
17741
- throw nothingFoundError(hash);
17742
- const node = await new globalThis.peernet.protos['peernet-file'](data);
17743
- await node.decode();
17744
- if (node.decoded?.links.length > 0)
17745
- throw new Error(`${hash} is a directory`);
17746
- if (options?.pin)
17747
- await dataStore.put(hash, node.encoded);
17748
- return node.decoded.content;
18215
+ const transfer = this.download(hash, { pin: options?.pin });
18216
+ return transfer.result;
17749
18217
  }
17750
18218
  /**
17751
18219
  * goes trough given stores and tries to find data for given hash