@leofcoin/chain 1.10.14 → 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.
- package/exports/browser/beacon-envelope.js +1 -1
- package/exports/browser/beacon-epoch.js +1 -1
- package/exports/browser/beacon-lifecycle.js +1 -1
- package/exports/browser/beacon-round.js +1 -1
- package/exports/browser/beacon-wire.js +2 -2
- package/exports/browser/beacon.js +1 -1
- package/exports/browser/{browser-DvU1xNFS-BJCQsckZ.js → browser-CCfR9KOM-CvhDZrGu.js} +3 -3
- package/exports/browser/{browser-crypto-Yk_e6OHw.js → browser-crypto-CeE-yId5.js} +284 -159
- package/exports/browser/chain.js +28 -11
- package/exports/browser/{client-DDC8C510-CUi3_3uT.js → client-CAC7RVMf-Dmz_2A_5.js} +4 -4
- package/exports/browser/{connection-monitor-BjXI0nBP.js → connection-monitor-Cmv00mwl.js} +5 -4
- package/exports/browser/{index-C_RakUAS.js → index-BC-KRBgE.js} +55 -52
- package/exports/browser/{index-Bxr5Iztg-AmkW0EHS.js → index-dTQVgkpf-CuHJWO2l.js} +3 -3
- package/exports/browser/{messages-DH0tgNM9-CZwrcL37.js → messages-Dw86z0zq-DrPw_nlZ.js} +9 -4
- package/exports/browser/{node-browser-CQugE1WZ.js → node-browser-DWSdi-XP.js} +950 -482
- package/exports/browser/node-browser.js +3 -3
- package/exports/browser/workers/block-worker.js +5 -3
- package/exports/browser/workers/machine-worker.js +24 -12
- package/exports/{workers/worker-DMCj1e6z-CTYVa2yX.js → browser/workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
- package/exports/chain.js +10 -5
- package/exports/{constants-CMYKv-Rt.js → constants-Dy9i9S_o.js} +1 -1
- package/exports/hash-format-state.js +17 -0
- package/exports/node.js +6 -1
- package/exports/workers/block-worker.js +5 -3
- package/exports/workers/machine-worker.js +24 -12
- package/exports/{browser/workers/worker-DMCj1e6z-CTYVa2yX.js → workers/worker-BDTWOCzS-COrAszCt.js} +304 -168
- package/package.json +6 -2
- package/exports/browser/browser-DvU1xNFS-4-PGqr4c.js +0 -151
- package/exports/browser/browser-DvU1xNFS-D9eSzJGJ.js +0 -151
- package/exports/browser/browser-crypto-BHeOz9rj.js +0 -8893
- package/exports/browser/browser-crypto-CTP3Yk4M.js +0 -8893
- package/exports/browser/client-DDC8C510-DbIdQjhy.js +0 -2225
- package/exports/browser/client-DDC8C510-ZN_MLo1Y.js +0 -2225
- package/exports/browser/connection-monitor-Cf0vNDCA.js +0 -453
- package/exports/browser/connection-monitor-pdopeM1I.js +0 -453
- package/exports/browser/hkdf-B9AXp_7K.js +0 -222
- package/exports/browser/index-Bxr5Iztg-Bdpw_E83.js +0 -40
- package/exports/browser/index-Bxr5Iztg-CxXVYm3t.js +0 -40
- package/exports/browser/index-CPQ5yvZ2.js +0 -4995
- package/exports/browser/index-uFaocBqT.js +0 -4995
- package/exports/browser/messages-DH0tgNM9-BRp0gcJh.js +0 -208
- package/exports/browser/messages-DH0tgNM9-DkNnpePH.js +0 -208
- package/exports/browser/node-browser-1VSuCeew.js +0 -17880
- package/exports/browser/node-browser-wDmRaOEo.js +0 -17880
- package/exports/browser/sha2-Dkoic3vb.js +0 -602
- package/exports/browser/weierstrass-HGdA_prK.js +0 -2156
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FormatInterface$2, T as TransactionMessage, C as ContractMessage, n as createHash$1 } from './browser-crypto-
|
|
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-
|
|
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$
|
|
402
|
-
const 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$
|
|
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$
|
|
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$
|
|
440
|
-
decode: decode$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
572
|
-
const decodeHex$1 = (string) => decode$
|
|
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$
|
|
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
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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 =
|
|
747
|
-
p:
|
|
748
|
-
n:
|
|
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
|
|
753
|
-
Gy
|
|
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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
|
-
|
|
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
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
-
|
|
799
|
-
|
|
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
|
-
/**
|
|
803
|
-
const bytesToHex = (
|
|
912
|
+
/** Convert byte array to hex string. */
|
|
913
|
+
const bytesToHex = (bytes) => {
|
|
804
914
|
let hex = '';
|
|
805
|
-
for (const
|
|
806
|
-
hex += padh(
|
|
915
|
+
for (const byte of abytes(bytes))
|
|
916
|
+
hex += padh(byte, 2);
|
|
807
917
|
return hex;
|
|
808
918
|
};
|
|
809
|
-
|
|
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
|
|
921
|
+
const e = 'hex invalid'; // Strict ASCII hex only, with one generic error for parse failures.
|
|
818
922
|
if (typeof hex !== 'string')
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
//
|
|
827
|
-
|
|
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 = () =>
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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) =>
|
|
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
|
-
|
|
968
|
+
throw new TypeError(msg);
|
|
857
969
|
if (min <= n && n < max)
|
|
858
970
|
return n;
|
|
859
|
-
|
|
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
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1003
|
+
const _hash = (name) => {
|
|
881
1004
|
// @ts-ignore
|
|
882
1005
|
const fn = hashes[name];
|
|
883
1006
|
if (typeof fn !== 'function')
|
|
884
|
-
|
|
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
|
|
890
|
-
const
|
|
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) =>
|
|
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 +
|
|
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) =>
|
|
912
|
-
/** lift_x from BIP340 returns the unique even square root
|
|
913
|
-
* SEC 1 callers still
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
1167
|
-
|
|
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 !==
|
|
1329
|
+
if (isCompressed === true && l !== 33)
|
|
1203
1330
|
return false;
|
|
1204
|
-
if (isCompressed === false && l !==
|
|
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
|
|
1233
|
-
|
|
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) =>
|
|
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(
|
|
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 <
|
|
1268
|
-
|
|
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__ */
|
|
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
|
|
1418
|
+
invert,
|
|
1296
1419
|
randomBytes,
|
|
1297
|
-
secretKeyToScalar
|
|
1298
|
-
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__ */
|
|
1311
|
-
isValidSecretKey
|
|
1312
|
-
isValidPublicKey
|
|
1313
|
-
randomSecretKey
|
|
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 =
|
|
1326
|
-
return
|
|
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) =>
|
|
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(
|
|
1342
|
-
const challengeAsync = async (...args) => bytesModN(await taggedHashAsync(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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.
|
|
1438
|
-
*
|
|
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__ */
|
|
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 <
|
|
1600
|
+
for (let w = 0; w < 33; w++) {
|
|
1479
1601
|
b = p;
|
|
1480
1602
|
points.push(b);
|
|
1481
|
-
for (let i = 1; 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
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
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 >
|
|
1523
|
-
wbits -=
|
|
1640
|
+
if (wbits > 128) {
|
|
1641
|
+
wbits -= 256;
|
|
1524
1642
|
n += 1n;
|
|
1525
1643
|
}
|
|
1526
|
-
const off = w *
|
|
1527
|
-
const offF = off; // offsets, evaluate both
|
|
1644
|
+
const off = w * 128;
|
|
1528
1645
|
const offP = off + Math.abs(wbits) - 1;
|
|
1529
|
-
const
|
|
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.
|
|
1533
|
-
f = f.add(ctneg(
|
|
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
|
-
|
|
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,
|
|
2577
|
+
const hash = await createHash(data, "SHA-256");
|
|
2461
2578
|
return (await createRIPEMD160())
|
|
2462
2579
|
.update(new Uint8Array(hash))
|
|
2463
|
-
.digest(
|
|
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(
|
|
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(
|
|
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(
|
|
2602
|
+
throw new TypeError("Seed should be at least 128 bits");
|
|
2486
2603
|
if (seed.length > 64)
|
|
2487
|
-
throw new TypeError(
|
|
2488
|
-
let hash = (await createHMAC(createSHA512(), new TextEncoder().encode(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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] ===
|
|
2688
|
+
let splitPath = path.split("/");
|
|
2689
|
+
if (splitPath[0] === "m") {
|
|
2573
2690
|
if (this.#parentFingerprint)
|
|
2574
|
-
throw new TypeError(
|
|
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(
|
|
2723
|
+
throw new TypeError("Invalid network version");
|
|
2607
2724
|
if (depth === 0) {
|
|
2608
2725
|
if (parentFingerprint !== 0)
|
|
2609
|
-
throw new TypeError(
|
|
2726
|
+
throw new TypeError("Invalid parent fingerprint");
|
|
2610
2727
|
}
|
|
2611
2728
|
if (depth === 0 && index !== 0)
|
|
2612
|
-
throw new TypeError(
|
|
2729
|
+
throw new TypeError("Invalid index");
|
|
2613
2730
|
if (version === network.bip32.private) {
|
|
2614
2731
|
if (k !== 0x00)
|
|
2615
|
-
throw new TypeError(
|
|
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$
|
|
4988
|
+
const decode$5 = base32.decode;
|
|
4872
4989
|
const decodeHex = base32Hex.decode;
|
|
4873
|
-
const 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$
|
|
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$
|
|
4891
|
-
encode: encode$
|
|
4892
|
-
decode: decode$
|
|
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
|
-
|
|
4968
|
-
|
|
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
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
const codec =
|
|
4988
|
-
|
|
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$
|
|
5129
|
+
return index$4.encode(this.multiSignature);
|
|
5017
5130
|
}
|
|
5018
5131
|
fromBs32(string) {
|
|
5019
|
-
return this.decode(index$
|
|
5132
|
+
return this.decode(index$4.decode(string));
|
|
5020
5133
|
}
|
|
5021
5134
|
toBs32Hex() {
|
|
5022
|
-
return index$
|
|
5135
|
+
return index$4.encodeHex(this.multiSignature);
|
|
5023
5136
|
}
|
|
5024
5137
|
fromBs32Hex(string) {
|
|
5025
|
-
return this.decode(index$
|
|
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
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
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[
|
|
5243
|
+
}, networks["leofcoin"]);
|
|
5122
5244
|
if (version !== this.network.version)
|
|
5123
|
-
throw new Error(
|
|
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-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
16349
|
-
uint8Array.set(this.discoCodec.codecBuffer
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
16398
|
-
this.
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
this.
|
|
16402
|
-
|
|
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
|
-
|
|
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.
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17738
|
-
|
|
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
|