@vbyte/btc-dev 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/LICENSE +21 -121
- package/README.md +36 -227
- package/dist/error.d.ts +11 -0
- package/dist/error.js +20 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/address/api.js +3 -2
- package/dist/lib/address/encode.js +6 -4
- package/dist/lib/address/p2pkh.js +4 -4
- package/dist/lib/address/p2sh.js +3 -3
- package/dist/lib/address/p2tr.js +3 -3
- package/dist/lib/address/p2wpkh.js +4 -4
- package/dist/lib/address/p2wsh.js +3 -3
- package/dist/lib/address/util.js +3 -1
- package/dist/lib/meta/locktime.js +3 -2
- package/dist/lib/meta/ref.js +4 -3
- package/dist/lib/meta/scribe.js +26 -6
- package/dist/lib/meta/sequence.js +8 -7
- package/dist/lib/script/decode.js +10 -9
- package/dist/lib/script/encode.js +4 -3
- package/dist/lib/script/words.js +4 -3
- package/dist/lib/sighash/segwit.js +9 -6
- package/dist/lib/sighash/taproot.js +7 -4
- package/dist/lib/sighash/util.js +4 -3
- package/dist/lib/signer/sign.js +7 -6
- package/dist/lib/signer/verify.js +8 -9
- package/dist/lib/taproot/cblock.js +3 -2
- package/dist/lib/taproot/encode.d.ts +1 -1
- package/dist/lib/taproot/encode.js +4 -3
- package/dist/lib/taproot/parse.js +9 -7
- package/dist/lib/taproot/tree.js +3 -2
- package/dist/lib/tx/create.js +1 -1
- package/dist/lib/tx/decode.js +13 -11
- package/dist/lib/tx/encode.js +1 -1
- package/dist/lib/tx/parse.js +3 -3
- package/dist/lib/tx/size.js +2 -4
- package/dist/lib/tx/util.js +2 -3
- package/dist/lib/tx/validate.js +36 -8
- package/dist/lib/witness/util.js +1 -1
- package/dist/main.cjs +1127 -1160
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +1125 -1161
- package/dist/module.mjs.map +1 -1
- package/dist/package.json +13 -11
- package/dist/script.js +10 -12
- package/dist/script.js.map +1 -1
- package/docs/API.md +1145 -0
- package/docs/CONVENTIONS.md +316 -0
- package/docs/FAQ.md +396 -0
- package/docs/GUIDE.md +1102 -0
- package/package.json +13 -11
package/dist/main.cjs
CHANGED
|
@@ -88,7 +88,10 @@ var Check;
|
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
90
90
|
else if (Array.isArray(input) &&
|
|
91
|
-
input.every(e => typeof e === 'number'
|
|
91
|
+
input.every(e => typeof e === 'number' &&
|
|
92
|
+
Number.isInteger(e) &&
|
|
93
|
+
e >= 0 &&
|
|
94
|
+
e <= 255)) {
|
|
92
95
|
return true;
|
|
93
96
|
}
|
|
94
97
|
else {
|
|
@@ -98,26 +101,57 @@ var Check;
|
|
|
98
101
|
Check.is_bytes = is_bytes;
|
|
99
102
|
})(Check || (Check = {}));
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
let ValidationError$1 = class ValidationError extends Error {
|
|
105
|
+
constructor(message) {
|
|
106
|
+
super(message);
|
|
107
|
+
this.name = 'ValidationError';
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
class HexValidationError extends ValidationError$1 {
|
|
111
|
+
constructor(message) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.name = 'HexValidationError';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class ByteRangeError extends ValidationError$1 {
|
|
117
|
+
constructor(message) {
|
|
118
|
+
super(message);
|
|
119
|
+
this.name = 'ByteRangeError';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
class IntegerBoundsError extends ValidationError$1 {
|
|
123
|
+
constructor(message) {
|
|
124
|
+
super(message);
|
|
125
|
+
this.name = 'IntegerBoundsError';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
class SizeError extends ValidationError$1 {
|
|
129
|
+
constructor(message) {
|
|
130
|
+
super(message);
|
|
131
|
+
this.name = 'SizeError';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var Assert$2;
|
|
102
136
|
(function (Assert) {
|
|
103
137
|
function within_size(data, size) {
|
|
104
138
|
if (data.length > size) {
|
|
105
|
-
throw new
|
|
139
|
+
throw new SizeError(`Data is larger than array size: ${data.length} > ${size}`);
|
|
106
140
|
}
|
|
107
141
|
}
|
|
108
142
|
Assert.within_size = within_size;
|
|
109
143
|
function is_hex(hex) {
|
|
110
144
|
if (hex.match(/[^a-fA-F0-9]/) !== null) {
|
|
111
|
-
throw new
|
|
145
|
+
throw new HexValidationError(`Invalid characters in hex string: ${hex}`);
|
|
112
146
|
}
|
|
113
147
|
if (hex.length % 2 !== 0) {
|
|
114
|
-
throw new
|
|
148
|
+
throw new HexValidationError(`Length of hex string is invalid: ${hex.length}`);
|
|
115
149
|
}
|
|
116
150
|
}
|
|
117
151
|
Assert.is_hex = is_hex;
|
|
118
152
|
function is_bytes(bytes) {
|
|
119
153
|
if (!Check.is_bytes(bytes)) {
|
|
120
|
-
throw new
|
|
154
|
+
throw new ByteRangeError(`Bytes contains invalid elements: ${String(bytes)}`);
|
|
121
155
|
}
|
|
122
156
|
}
|
|
123
157
|
Assert.is_bytes = is_bytes;
|
|
@@ -126,17 +160,17 @@ var Assert$1;
|
|
|
126
160
|
JSON.parse(str);
|
|
127
161
|
}
|
|
128
162
|
catch {
|
|
129
|
-
throw new
|
|
163
|
+
throw new ValidationError$1('JSON string is invalid!');
|
|
130
164
|
}
|
|
131
165
|
}
|
|
132
166
|
Assert.is_json = is_json;
|
|
133
167
|
function is_safe_int(num) {
|
|
134
|
-
if (num > Number.MAX_SAFE_INTEGER) {
|
|
135
|
-
throw new
|
|
168
|
+
if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
|
|
169
|
+
throw new IntegerBoundsError('Number exceeds safe bounds!');
|
|
136
170
|
}
|
|
137
171
|
}
|
|
138
172
|
Assert.is_safe_int = is_safe_int;
|
|
139
|
-
})(Assert$
|
|
173
|
+
})(Assert$2 || (Assert$2 = {}));
|
|
140
174
|
|
|
141
175
|
const _0n$7 = BigInt(0);
|
|
142
176
|
const _255n = BigInt(255);
|
|
@@ -230,7 +264,7 @@ function bytes_to_num(bytes) {
|
|
|
230
264
|
let num = 0;
|
|
231
265
|
for (let i = bytes.length - 1; i >= 0; i--) {
|
|
232
266
|
num = (num * 256) + bytes[i];
|
|
233
|
-
Assert$
|
|
267
|
+
Assert$2.is_safe_int(num);
|
|
234
268
|
}
|
|
235
269
|
return num;
|
|
236
270
|
}
|
|
@@ -261,7 +295,7 @@ function bytes_to_hex(bytes) {
|
|
|
261
295
|
return chars;
|
|
262
296
|
}
|
|
263
297
|
function get_hex_size(hexstr, size) {
|
|
264
|
-
Assert$
|
|
298
|
+
Assert$2.is_hex(hexstr);
|
|
265
299
|
const len = hexstr.length / 2;
|
|
266
300
|
if (size === undefined)
|
|
267
301
|
size = len;
|
|
@@ -279,7 +313,7 @@ function buffer(bytes, size, endian) {
|
|
|
279
313
|
return create_bytes(bytes, size, endian);
|
|
280
314
|
}
|
|
281
315
|
else if (typeof bytes === 'string') {
|
|
282
|
-
Assert$
|
|
316
|
+
Assert$2.is_hex(bytes);
|
|
283
317
|
return hex_to_bytes(bytes, size, endian);
|
|
284
318
|
}
|
|
285
319
|
else if (typeof bytes === 'bigint') {
|
|
@@ -293,7 +327,7 @@ function buffer(bytes, size, endian) {
|
|
|
293
327
|
function create_bytes(data, size, endian = 'le') {
|
|
294
328
|
if (size === undefined)
|
|
295
329
|
size = data.length;
|
|
296
|
-
Assert$
|
|
330
|
+
Assert$2.within_size(data, size);
|
|
297
331
|
const buffer = new Uint8Array(size).fill(0);
|
|
298
332
|
const offset = (endian === 'be') ? 0 : size - data.length;
|
|
299
333
|
buffer.set(data, offset);
|
|
@@ -370,10 +404,14 @@ function bytes_to_str(bytes) {
|
|
|
370
404
|
return dc.decode(bytes);
|
|
371
405
|
}
|
|
372
406
|
|
|
407
|
+
const MAX_RANDOM_LENGTH = 1024 * 1024;
|
|
373
408
|
function get_random_bytes(length = 32) {
|
|
374
409
|
if (!Number.isInteger(length) || length < 0) {
|
|
375
410
|
throw new TypeError('Length must be a non-negative integer');
|
|
376
411
|
}
|
|
412
|
+
if (length > MAX_RANDOM_LENGTH) {
|
|
413
|
+
throw new RangeError(`Length exceeds maximum allowed: ${length} > ${MAX_RANDOM_LENGTH}`);
|
|
414
|
+
}
|
|
377
415
|
if (crypto &&
|
|
378
416
|
typeof crypto.getRandomValues === 'function') {
|
|
379
417
|
return crypto.getRandomValues(new Uint8Array(length));
|
|
@@ -406,11 +444,11 @@ class Buff extends Uint8Array {
|
|
|
406
444
|
return new Buff(uint, size, endian);
|
|
407
445
|
}; }
|
|
408
446
|
static { this.hex = (data, size, endian) => {
|
|
409
|
-
Assert$
|
|
447
|
+
Assert$2.is_hex(data);
|
|
410
448
|
return new Buff(data, size, endian);
|
|
411
449
|
}; }
|
|
412
450
|
static { this.bytes = (bytes, size, endian) => {
|
|
413
|
-
Assert$
|
|
451
|
+
Assert$2.is_bytes(bytes);
|
|
414
452
|
return new Buff(bytes, size, endian);
|
|
415
453
|
}; }
|
|
416
454
|
static { this.json = (data, replacer) => {
|
|
@@ -425,7 +463,15 @@ class Buff extends Uint8Array {
|
|
|
425
463
|
return chunks.map(e => new Buff(e));
|
|
426
464
|
}; }
|
|
427
465
|
static { this.is_equal = (a, b) => {
|
|
428
|
-
|
|
466
|
+
const buf_a = new Buff(a);
|
|
467
|
+
const buf_b = new Buff(b);
|
|
468
|
+
if (buf_a.length !== buf_b.length)
|
|
469
|
+
return false;
|
|
470
|
+
for (let i = 0; i < buf_a.length; i++) {
|
|
471
|
+
if (buf_a[i] !== buf_b[i])
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
return true;
|
|
429
475
|
}; }
|
|
430
476
|
static { this.is_bytes = Check.is_bytes; }
|
|
431
477
|
static { this.is_hex = Check.is_hex; }
|
|
@@ -494,7 +540,14 @@ class Buff extends Uint8Array {
|
|
|
494
540
|
return Buff.join([this, new Buff(data)]);
|
|
495
541
|
}
|
|
496
542
|
equals(data) {
|
|
497
|
-
|
|
543
|
+
const other = new Buff(data);
|
|
544
|
+
if (this.length !== other.length)
|
|
545
|
+
return false;
|
|
546
|
+
for (let i = 0; i < this.length; i++) {
|
|
547
|
+
if (this[i] !== other[i])
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
return true;
|
|
498
551
|
}
|
|
499
552
|
prepend(data) {
|
|
500
553
|
return Buff.join([new Buff(data), this]);
|
|
@@ -543,6 +596,9 @@ class Buff extends Uint8Array {
|
|
|
543
596
|
return hex.map(e => Buff.hex(e, size));
|
|
544
597
|
}
|
|
545
598
|
static create_varint(num, endian) {
|
|
599
|
+
if (num < 0) {
|
|
600
|
+
throw new Error(`Varint cannot be negative: ${num}`);
|
|
601
|
+
}
|
|
546
602
|
if (num < 0xFD) {
|
|
547
603
|
return Buff.num(num, 1);
|
|
548
604
|
}
|
|
@@ -595,6 +651,27 @@ class Stream {
|
|
|
595
651
|
}
|
|
596
652
|
}
|
|
597
653
|
|
|
654
|
+
class ValidationError extends Error {
|
|
655
|
+
constructor(message, field) {
|
|
656
|
+
super(message);
|
|
657
|
+
this.name = "ValidationError";
|
|
658
|
+
this.field = field;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
class DecodingError extends Error {
|
|
662
|
+
constructor(message, position) {
|
|
663
|
+
super(message);
|
|
664
|
+
this.name = "DecodingError";
|
|
665
|
+
this.position = position;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
class ConfigError extends Error {
|
|
669
|
+
constructor(message) {
|
|
670
|
+
super(message);
|
|
671
|
+
this.name = "ConfigError";
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
598
675
|
function is_return_script(script) {
|
|
599
676
|
const bytes = Buff.bytes(script);
|
|
600
677
|
return bytes.at(0) === 0x6a;
|
|
@@ -649,14 +726,20 @@ function is_opreturn_script(script) {
|
|
|
649
726
|
return LOCK_SCRIPT_REGEX.opreturn.test(hex);
|
|
650
727
|
}
|
|
651
728
|
|
|
652
|
-
function
|
|
653
|
-
if (obj
|
|
729
|
+
function sort(obj) {
|
|
730
|
+
if (obj === null ||
|
|
731
|
+
obj === undefined ||
|
|
732
|
+
obj instanceof Map ||
|
|
733
|
+
obj instanceof Set ||
|
|
734
|
+
obj instanceof Date ||
|
|
735
|
+
Array.isArray(obj) ||
|
|
736
|
+
typeof obj !== 'object') {
|
|
654
737
|
return obj;
|
|
655
738
|
}
|
|
656
739
|
else {
|
|
657
740
|
return Object.keys(obj)
|
|
658
741
|
.sort()
|
|
659
|
-
.filter(
|
|
742
|
+
.filter(key => obj[key] !== undefined)
|
|
660
743
|
.reduce((sorted, key) => {
|
|
661
744
|
sorted[key] = obj[key];
|
|
662
745
|
return sorted;
|
|
@@ -683,10 +766,10 @@ var Test;
|
|
|
683
766
|
Test.is_object = is_object;
|
|
684
767
|
function is_deep_equal(a, b) {
|
|
685
768
|
if (is_object(a))
|
|
686
|
-
a =
|
|
769
|
+
a = sort(a);
|
|
687
770
|
if (is_object(b))
|
|
688
|
-
b =
|
|
689
|
-
return
|
|
771
|
+
b = sort(b);
|
|
772
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
690
773
|
}
|
|
691
774
|
Test.is_deep_equal = is_deep_equal;
|
|
692
775
|
function has_items(array) {
|
|
@@ -698,33 +781,33 @@ var Test;
|
|
|
698
781
|
}
|
|
699
782
|
Test.is_string = is_string;
|
|
700
783
|
function is_number(value) {
|
|
701
|
-
return
|
|
784
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
702
785
|
}
|
|
703
786
|
Test.is_number = is_number;
|
|
787
|
+
function is_integer(value) {
|
|
788
|
+
return Number.isInteger(value);
|
|
789
|
+
}
|
|
790
|
+
Test.is_integer = is_integer;
|
|
704
791
|
function is_bigint(value) {
|
|
705
792
|
return typeof value === 'bigint';
|
|
706
793
|
}
|
|
707
794
|
Test.is_bigint = is_bigint;
|
|
708
795
|
function is_uchar(value) {
|
|
709
|
-
return
|
|
796
|
+
return is_integer(value) && value >= 0 && value <= 0xFF;
|
|
710
797
|
}
|
|
711
798
|
Test.is_uchar = is_uchar;
|
|
712
799
|
function is_ushort(value) {
|
|
713
|
-
return
|
|
800
|
+
return is_integer(value) && value >= 0 && value <= 0xFFFF;
|
|
714
801
|
}
|
|
715
802
|
Test.is_ushort = is_ushort;
|
|
716
803
|
function is_uint(value) {
|
|
717
|
-
return
|
|
804
|
+
return is_integer(value) && value >= 0 && value <= 0xFFFFFFFF;
|
|
718
805
|
}
|
|
719
806
|
Test.is_uint = is_uint;
|
|
720
807
|
function is_u8a(value) {
|
|
721
808
|
return value instanceof Uint8Array;
|
|
722
809
|
}
|
|
723
810
|
Test.is_u8a = is_u8a;
|
|
724
|
-
function is_bytes(value) {
|
|
725
|
-
return Buff.is_bytes(value);
|
|
726
|
-
}
|
|
727
|
-
Test.is_bytes = is_bytes;
|
|
728
811
|
function is_base58(value) {
|
|
729
812
|
if (typeof value !== 'string')
|
|
730
813
|
return false;
|
|
@@ -759,9 +842,33 @@ var Test;
|
|
|
759
842
|
return (is_string(value) && is_hex(value) && value.length === 64);
|
|
760
843
|
}
|
|
761
844
|
Test.is_hash = is_hash;
|
|
845
|
+
function is_null(value) {
|
|
846
|
+
return value === null;
|
|
847
|
+
}
|
|
848
|
+
Test.is_null = is_null;
|
|
849
|
+
function is_undefined(value) {
|
|
850
|
+
return value === undefined;
|
|
851
|
+
}
|
|
852
|
+
Test.is_undefined = is_undefined;
|
|
853
|
+
function is_array(value) {
|
|
854
|
+
return Array.isArray(value);
|
|
855
|
+
}
|
|
856
|
+
Test.is_array = is_array;
|
|
857
|
+
function is_function(value) {
|
|
858
|
+
return typeof value === 'function';
|
|
859
|
+
}
|
|
860
|
+
Test.is_function = is_function;
|
|
861
|
+
function is_error(value) {
|
|
862
|
+
return value instanceof Error;
|
|
863
|
+
}
|
|
864
|
+
Test.is_error = is_error;
|
|
865
|
+
function is_promise(value) {
|
|
866
|
+
return value instanceof Promise || (is_object(value) && is_function(value.then) && is_function(value.catch));
|
|
867
|
+
}
|
|
868
|
+
Test.is_promise = is_promise;
|
|
762
869
|
})(Test || (Test = {}));
|
|
763
870
|
|
|
764
|
-
var Assert;
|
|
871
|
+
var Assert$1;
|
|
765
872
|
(function (Assert) {
|
|
766
873
|
function ok(value, message) {
|
|
767
874
|
if (value === false) {
|
|
@@ -811,6 +918,12 @@ var Assert;
|
|
|
811
918
|
}
|
|
812
919
|
}
|
|
813
920
|
Assert.is_number = is_number;
|
|
921
|
+
function is_integer(value) {
|
|
922
|
+
if (!Test.is_integer(value)) {
|
|
923
|
+
throw new TypeError(`invalid integer: ${String(value)}`);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
Assert.is_integer = is_integer;
|
|
814
927
|
function is_bigint(value) {
|
|
815
928
|
if (!Test.is_bigint(value)) {
|
|
816
929
|
throw new TypeError(`invalid bigint: ${String(value)}`);
|
|
@@ -853,19 +966,6 @@ var Assert;
|
|
|
853
966
|
}
|
|
854
967
|
}
|
|
855
968
|
Assert.is_hash = is_hash;
|
|
856
|
-
function is_bytes(value, msg) {
|
|
857
|
-
if (!Test.is_bytes(value)) {
|
|
858
|
-
throw new TypeError(msg ?? `invalid bytes: ${String(value)}`);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
Assert.is_bytes = is_bytes;
|
|
862
|
-
function size(input, size, msg) {
|
|
863
|
-
const bytes = Buff.bytes(input);
|
|
864
|
-
if (bytes.length !== size) {
|
|
865
|
-
throw new Error(msg ?? `invalid input size: ${bytes.length} !== ${size}`);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
Assert.size = size;
|
|
869
969
|
function has_items(array, err_msg) {
|
|
870
970
|
if (!Test.has_items(array)) {
|
|
871
971
|
throw new Error(err_msg ?? 'array does not contain any items');
|
|
@@ -896,41 +996,41 @@ var Assert;
|
|
|
896
996
|
}
|
|
897
997
|
}
|
|
898
998
|
Assert.is_bech32 = is_bech32;
|
|
899
|
-
})(Assert || (Assert = {}));
|
|
900
|
-
|
|
901
|
-
const crypto$1 = typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
|
|
999
|
+
})(Assert$1 || (Assert$1 = {}));
|
|
902
1000
|
|
|
903
1001
|
/**
|
|
904
1002
|
* Utilities for hex, bytes, CSPRNG.
|
|
905
1003
|
* @module
|
|
906
1004
|
*/
|
|
907
1005
|
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
908
|
-
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
|
|
909
|
-
// node.js versions earlier than v19 don't declare it in global scope.
|
|
910
|
-
// For node.js, package.json#exports field mapping rewrites import
|
|
911
|
-
// from `crypto` to `cryptoNode`, which imports native module.
|
|
912
|
-
// Makes the utils un-importable in browsers without a bundler.
|
|
913
|
-
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
|
|
914
1006
|
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
|
|
915
1007
|
function isBytes$1(a) {
|
|
916
1008
|
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
917
1009
|
}
|
|
918
1010
|
/** Asserts something is positive integer. */
|
|
919
|
-
function anumber$1(n) {
|
|
920
|
-
if (!Number.isSafeInteger(n) || n < 0)
|
|
921
|
-
|
|
1011
|
+
function anumber$1(n, title = '') {
|
|
1012
|
+
if (!Number.isSafeInteger(n) || n < 0) {
|
|
1013
|
+
const prefix = title && `"${title}" `;
|
|
1014
|
+
throw new Error(`${prefix}expected integer >= 0, got ${n}`);
|
|
1015
|
+
}
|
|
922
1016
|
}
|
|
923
1017
|
/** Asserts something is Uint8Array. */
|
|
924
|
-
function abytes$1(
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1018
|
+
function abytes$1(value, length, title = '') {
|
|
1019
|
+
const bytes = isBytes$1(value);
|
|
1020
|
+
const len = value?.length;
|
|
1021
|
+
const needsLen = length !== undefined;
|
|
1022
|
+
if (!bytes || (needsLen && len !== length)) {
|
|
1023
|
+
const prefix = title && `"${title}" `;
|
|
1024
|
+
const ofLen = needsLen ? ` of length ${length}` : '';
|
|
1025
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
1026
|
+
throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);
|
|
1027
|
+
}
|
|
1028
|
+
return value;
|
|
929
1029
|
}
|
|
930
1030
|
/** Asserts something is hash */
|
|
931
1031
|
function ahash(h) {
|
|
932
1032
|
if (typeof h !== 'function' || typeof h.create !== 'function')
|
|
933
|
-
throw new Error('Hash
|
|
1033
|
+
throw new Error('Hash must wrapped by utils.createHasher');
|
|
934
1034
|
anumber$1(h.outputLen);
|
|
935
1035
|
anumber$1(h.blockLen);
|
|
936
1036
|
}
|
|
@@ -943,10 +1043,10 @@ function aexists(instance, checkFinished = true) {
|
|
|
943
1043
|
}
|
|
944
1044
|
/** Asserts output is properly-sized byte array */
|
|
945
1045
|
function aoutput(out, instance) {
|
|
946
|
-
abytes$1(out);
|
|
1046
|
+
abytes$1(out, undefined, 'digestInto() output');
|
|
947
1047
|
const min = instance.outputLen;
|
|
948
1048
|
if (out.length < min) {
|
|
949
|
-
throw new Error('digestInto()
|
|
1049
|
+
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|
950
1050
|
}
|
|
951
1051
|
}
|
|
952
1052
|
/** Zeroize a byte array. Warning: JS provides no guarantees. */
|
|
@@ -1026,26 +1126,6 @@ function hexToBytes(hex) {
|
|
|
1026
1126
|
}
|
|
1027
1127
|
return array;
|
|
1028
1128
|
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Converts string to bytes using UTF8 encoding.
|
|
1031
|
-
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
|
|
1032
|
-
*/
|
|
1033
|
-
function utf8ToBytes(str) {
|
|
1034
|
-
if (typeof str !== 'string')
|
|
1035
|
-
throw new Error('string expected');
|
|
1036
|
-
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
|
|
1040
|
-
* Warning: when Uint8Array is passed, it would NOT get copied.
|
|
1041
|
-
* Keep in mind for future mutable operations.
|
|
1042
|
-
*/
|
|
1043
|
-
function toBytes(data) {
|
|
1044
|
-
if (typeof data === 'string')
|
|
1045
|
-
data = utf8ToBytes(data);
|
|
1046
|
-
abytes$1(data);
|
|
1047
|
-
return data;
|
|
1048
|
-
}
|
|
1049
1129
|
/** Copies several Uint8Arrays into one. */
|
|
1050
1130
|
function concatBytes(...arrays) {
|
|
1051
1131
|
let sum = 0;
|
|
@@ -1062,47 +1142,121 @@ function concatBytes(...arrays) {
|
|
|
1062
1142
|
}
|
|
1063
1143
|
return res;
|
|
1064
1144
|
}
|
|
1065
|
-
/**
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
function createHasher(hashCons) {
|
|
1070
|
-
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
1071
|
-
const tmp = hashCons();
|
|
1145
|
+
/** Creates function with outputLen, blockLen, create properties from a class constructor. */
|
|
1146
|
+
function createHasher(hashCons, info = {}) {
|
|
1147
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
1148
|
+
const tmp = hashCons(undefined);
|
|
1072
1149
|
hashC.outputLen = tmp.outputLen;
|
|
1073
1150
|
hashC.blockLen = tmp.blockLen;
|
|
1074
|
-
hashC.create = () => hashCons();
|
|
1075
|
-
|
|
1151
|
+
hashC.create = (opts) => hashCons(opts);
|
|
1152
|
+
Object.assign(hashC, info);
|
|
1153
|
+
return Object.freeze(hashC);
|
|
1076
1154
|
}
|
|
1077
1155
|
/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */
|
|
1078
1156
|
function randomBytes(bytesLength = 32) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1157
|
+
const cr = typeof globalThis === 'object' ? globalThis.crypto : null;
|
|
1158
|
+
if (typeof cr?.getRandomValues !== 'function')
|
|
1159
|
+
throw new Error('crypto.getRandomValues must be defined');
|
|
1160
|
+
return cr.getRandomValues(new Uint8Array(bytesLength));
|
|
1161
|
+
}
|
|
1162
|
+
/** Creates OID opts for NIST hashes, with prefix 06 09 60 86 48 01 65 03 04 02. */
|
|
1163
|
+
const oidNist = (suffix) => ({
|
|
1164
|
+
oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* HMAC: RFC2104 message authentication code.
|
|
1169
|
+
* @module
|
|
1170
|
+
*/
|
|
1171
|
+
/** Internal class for HMAC. */
|
|
1172
|
+
class _HMAC {
|
|
1173
|
+
oHash;
|
|
1174
|
+
iHash;
|
|
1175
|
+
blockLen;
|
|
1176
|
+
outputLen;
|
|
1177
|
+
finished = false;
|
|
1178
|
+
destroyed = false;
|
|
1179
|
+
constructor(hash, key) {
|
|
1180
|
+
ahash(hash);
|
|
1181
|
+
abytes$1(key, undefined, 'key');
|
|
1182
|
+
this.iHash = hash.create();
|
|
1183
|
+
if (typeof this.iHash.update !== 'function')
|
|
1184
|
+
throw new Error('Expected instance of class which extends utils.Hash');
|
|
1185
|
+
this.blockLen = this.iHash.blockLen;
|
|
1186
|
+
this.outputLen = this.iHash.outputLen;
|
|
1187
|
+
const blockLen = this.blockLen;
|
|
1188
|
+
const pad = new Uint8Array(blockLen);
|
|
1189
|
+
// blockLen can be bigger than outputLen
|
|
1190
|
+
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|
1191
|
+
for (let i = 0; i < pad.length; i++)
|
|
1192
|
+
pad[i] ^= 0x36;
|
|
1193
|
+
this.iHash.update(pad);
|
|
1194
|
+
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
|
|
1195
|
+
this.oHash = hash.create();
|
|
1196
|
+
// Undo internal XOR && apply outer XOR
|
|
1197
|
+
for (let i = 0; i < pad.length; i++)
|
|
1198
|
+
pad[i] ^= 0x36 ^ 0x5c;
|
|
1199
|
+
this.oHash.update(pad);
|
|
1200
|
+
clean(pad);
|
|
1081
1201
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1202
|
+
update(buf) {
|
|
1203
|
+
aexists(this);
|
|
1204
|
+
this.iHash.update(buf);
|
|
1205
|
+
return this;
|
|
1206
|
+
}
|
|
1207
|
+
digestInto(out) {
|
|
1208
|
+
aexists(this);
|
|
1209
|
+
abytes$1(out, this.outputLen, 'output');
|
|
1210
|
+
this.finished = true;
|
|
1211
|
+
this.iHash.digestInto(out);
|
|
1212
|
+
this.oHash.update(out);
|
|
1213
|
+
this.oHash.digestInto(out);
|
|
1214
|
+
this.destroy();
|
|
1215
|
+
}
|
|
1216
|
+
digest() {
|
|
1217
|
+
const out = new Uint8Array(this.oHash.outputLen);
|
|
1218
|
+
this.digestInto(out);
|
|
1219
|
+
return out;
|
|
1220
|
+
}
|
|
1221
|
+
_cloneInto(to) {
|
|
1222
|
+
// Create new instance without calling constructor since key already in state and we don't know it.
|
|
1223
|
+
to ||= Object.create(Object.getPrototypeOf(this), {});
|
|
1224
|
+
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|
1225
|
+
to = to;
|
|
1226
|
+
to.finished = finished;
|
|
1227
|
+
to.destroyed = destroyed;
|
|
1228
|
+
to.blockLen = blockLen;
|
|
1229
|
+
to.outputLen = outputLen;
|
|
1230
|
+
to.oHash = oHash._cloneInto(to.oHash);
|
|
1231
|
+
to.iHash = iHash._cloneInto(to.iHash);
|
|
1232
|
+
return to;
|
|
1233
|
+
}
|
|
1234
|
+
clone() {
|
|
1235
|
+
return this._cloneInto();
|
|
1236
|
+
}
|
|
1237
|
+
destroy() {
|
|
1238
|
+
this.destroyed = true;
|
|
1239
|
+
this.oHash.destroy();
|
|
1240
|
+
this.iHash.destroy();
|
|
1085
1241
|
}
|
|
1086
|
-
throw new Error('crypto.getRandomValues must be defined');
|
|
1087
1242
|
}
|
|
1243
|
+
/**
|
|
1244
|
+
* HMAC: RFC2104 message authentication code.
|
|
1245
|
+
* @param hash - function that would be used e.g. sha256
|
|
1246
|
+
* @param key - message key
|
|
1247
|
+
* @param message - message data
|
|
1248
|
+
* @example
|
|
1249
|
+
* import { hmac } from '@noble/hashes/hmac';
|
|
1250
|
+
* import { sha256 } from '@noble/hashes/sha2';
|
|
1251
|
+
* const mac1 = hmac(sha256, 'key', 'message');
|
|
1252
|
+
*/
|
|
1253
|
+
const hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest();
|
|
1254
|
+
hmac.create = (hash, key) => new _HMAC(hash, key);
|
|
1088
1255
|
|
|
1089
1256
|
/**
|
|
1090
1257
|
* Internal Merkle-Damgard hash utils.
|
|
1091
1258
|
* @module
|
|
1092
1259
|
*/
|
|
1093
|
-
/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */
|
|
1094
|
-
function setBigUint64(view, byteOffset, value, isLE) {
|
|
1095
|
-
if (typeof view.setBigUint64 === 'function')
|
|
1096
|
-
return view.setBigUint64(byteOffset, value, isLE);
|
|
1097
|
-
const _32n = BigInt(32);
|
|
1098
|
-
const _u32_max = BigInt(0xffffffff);
|
|
1099
|
-
const wh = Number((value >> _32n) & _u32_max);
|
|
1100
|
-
const wl = Number(value & _u32_max);
|
|
1101
|
-
const h = isLE ? 4 : 0;
|
|
1102
|
-
const l = isLE ? 0 : 4;
|
|
1103
|
-
view.setUint32(byteOffset + h, wh, isLE);
|
|
1104
|
-
view.setUint32(byteOffset + l, wl, isLE);
|
|
1105
|
-
}
|
|
1106
1260
|
/** Choice: a ? b : c */
|
|
1107
1261
|
function Chi(a, b, c) {
|
|
1108
1262
|
return (a & b) ^ (~a & c);
|
|
@@ -1115,13 +1269,19 @@ function Maj(a, b, c) {
|
|
|
1115
1269
|
* Merkle-Damgard hash construction base class.
|
|
1116
1270
|
* Could be used to create MD5, RIPEMD, SHA1, SHA2.
|
|
1117
1271
|
*/
|
|
1118
|
-
class HashMD
|
|
1272
|
+
class HashMD {
|
|
1273
|
+
blockLen;
|
|
1274
|
+
outputLen;
|
|
1275
|
+
padOffset;
|
|
1276
|
+
isLE;
|
|
1277
|
+
// For partial updates less than block size
|
|
1278
|
+
buffer;
|
|
1279
|
+
view;
|
|
1280
|
+
finished = false;
|
|
1281
|
+
length = 0;
|
|
1282
|
+
pos = 0;
|
|
1283
|
+
destroyed = false;
|
|
1119
1284
|
constructor(blockLen, outputLen, padOffset, isLE) {
|
|
1120
|
-
super();
|
|
1121
|
-
this.finished = false;
|
|
1122
|
-
this.length = 0;
|
|
1123
|
-
this.pos = 0;
|
|
1124
|
-
this.destroyed = false;
|
|
1125
1285
|
this.blockLen = blockLen;
|
|
1126
1286
|
this.outputLen = outputLen;
|
|
1127
1287
|
this.padOffset = padOffset;
|
|
@@ -1131,7 +1291,6 @@ class HashMD extends Hash {
|
|
|
1131
1291
|
}
|
|
1132
1292
|
update(data) {
|
|
1133
1293
|
aexists(this);
|
|
1134
|
-
data = toBytes(data);
|
|
1135
1294
|
abytes$1(data);
|
|
1136
1295
|
const { view, buffer, blockLen } = this;
|
|
1137
1296
|
const len = data.length;
|
|
@@ -1180,13 +1339,13 @@ class HashMD extends Hash {
|
|
|
1180
1339
|
// Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that
|
|
1181
1340
|
// You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.
|
|
1182
1341
|
// So we just write lowest 64 bits of that value.
|
|
1183
|
-
setBigUint64(
|
|
1342
|
+
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
|
|
1184
1343
|
this.process(view, 0);
|
|
1185
1344
|
const oview = createView(out);
|
|
1186
1345
|
const len = this.outputLen;
|
|
1187
|
-
// NOTE: we do division by 4 later, which
|
|
1346
|
+
// NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT
|
|
1188
1347
|
if (len % 4)
|
|
1189
|
-
throw new Error('_sha2: outputLen
|
|
1348
|
+
throw new Error('_sha2: outputLen must be aligned to 32bit');
|
|
1190
1349
|
const outLen = len / 4;
|
|
1191
1350
|
const state = this.get();
|
|
1192
1351
|
if (outLen > state.length)
|
|
@@ -1202,7 +1361,7 @@ class HashMD extends Hash {
|
|
|
1202
1361
|
return res;
|
|
1203
1362
|
}
|
|
1204
1363
|
_cloneInto(to) {
|
|
1205
|
-
to
|
|
1364
|
+
to ||= new this.constructor();
|
|
1206
1365
|
to.set(...this.get());
|
|
1207
1366
|
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|
1208
1367
|
to.destroyed = destroyed;
|
|
@@ -1226,10 +1385,128 @@ const SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|
|
1226
1385
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
1227
1386
|
]);
|
|
1228
1387
|
|
|
1388
|
+
/**
|
|
1389
|
+
|
|
1390
|
+
SHA1 (RFC 3174), MD5 (RFC 1321) and RIPEMD160 (RFC 2286) legacy, weak hash functions.
|
|
1391
|
+
Don't use them in a new protocol. What "weak" means:
|
|
1392
|
+
|
|
1393
|
+
- Collisions can be made with 2^18 effort in MD5, 2^60 in SHA1, 2^80 in RIPEMD160.
|
|
1394
|
+
- No practical pre-image attacks (only theoretical, 2^123.4)
|
|
1395
|
+
- HMAC seems kinda ok: https://www.rfc-editor.org/rfc/rfc6151
|
|
1396
|
+
* @module
|
|
1397
|
+
*/
|
|
1398
|
+
// RIPEMD-160
|
|
1399
|
+
const Rho160 = /* @__PURE__ */ Uint8Array.from([
|
|
1400
|
+
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
1401
|
+
]);
|
|
1402
|
+
const Id160 = /* @__PURE__ */ (() => Uint8Array.from(new Array(16).fill(0).map((_, i) => i)))();
|
|
1403
|
+
const Pi160 = /* @__PURE__ */ (() => Id160.map((i) => (9 * i + 5) % 16))();
|
|
1404
|
+
const idxLR = /* @__PURE__ */ (() => {
|
|
1405
|
+
const L = [Id160];
|
|
1406
|
+
const R = [Pi160];
|
|
1407
|
+
const res = [L, R];
|
|
1408
|
+
for (let i = 0; i < 4; i++)
|
|
1409
|
+
for (let j of res)
|
|
1410
|
+
j.push(j[i].map((k) => Rho160[k]));
|
|
1411
|
+
return res;
|
|
1412
|
+
})();
|
|
1413
|
+
const idxL = /* @__PURE__ */ (() => idxLR[0])();
|
|
1414
|
+
const idxR = /* @__PURE__ */ (() => idxLR[1])();
|
|
1415
|
+
// const [idxL, idxR] = idxLR;
|
|
1416
|
+
const shifts160 = /* @__PURE__ */ [
|
|
1417
|
+
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
|
|
1418
|
+
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
|
|
1419
|
+
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
|
|
1420
|
+
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
|
|
1421
|
+
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5],
|
|
1422
|
+
].map((i) => Uint8Array.from(i));
|
|
1423
|
+
const shiftsL160 = /* @__PURE__ */ idxL.map((idx, i) => idx.map((j) => shifts160[i][j]));
|
|
1424
|
+
const shiftsR160 = /* @__PURE__ */ idxR.map((idx, i) => idx.map((j) => shifts160[i][j]));
|
|
1425
|
+
const Kl160 = /* @__PURE__ */ Uint32Array.from([
|
|
1426
|
+
0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e,
|
|
1427
|
+
]);
|
|
1428
|
+
const Kr160 = /* @__PURE__ */ Uint32Array.from([
|
|
1429
|
+
0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000,
|
|
1430
|
+
]);
|
|
1431
|
+
// It's called f() in spec.
|
|
1432
|
+
function ripemd_f(group, x, y, z) {
|
|
1433
|
+
if (group === 0)
|
|
1434
|
+
return x ^ y ^ z;
|
|
1435
|
+
if (group === 1)
|
|
1436
|
+
return (x & y) | (~x & z);
|
|
1437
|
+
if (group === 2)
|
|
1438
|
+
return (x | ~y) ^ z;
|
|
1439
|
+
if (group === 3)
|
|
1440
|
+
return (x & z) | (y & ~z);
|
|
1441
|
+
return x ^ (y | ~z);
|
|
1442
|
+
}
|
|
1443
|
+
// Reusable temporary buffer
|
|
1444
|
+
const BUF_160 = /* @__PURE__ */ new Uint32Array(16);
|
|
1445
|
+
class _RIPEMD160 extends HashMD {
|
|
1446
|
+
h0 = 0x67452301 | 0;
|
|
1447
|
+
h1 = 0xefcdab89 | 0;
|
|
1448
|
+
h2 = 0x98badcfe | 0;
|
|
1449
|
+
h3 = 0x10325476 | 0;
|
|
1450
|
+
h4 = 0xc3d2e1f0 | 0;
|
|
1451
|
+
constructor() {
|
|
1452
|
+
super(64, 20, 8, true);
|
|
1453
|
+
}
|
|
1454
|
+
get() {
|
|
1455
|
+
const { h0, h1, h2, h3, h4 } = this;
|
|
1456
|
+
return [h0, h1, h2, h3, h4];
|
|
1457
|
+
}
|
|
1458
|
+
set(h0, h1, h2, h3, h4) {
|
|
1459
|
+
this.h0 = h0 | 0;
|
|
1460
|
+
this.h1 = h1 | 0;
|
|
1461
|
+
this.h2 = h2 | 0;
|
|
1462
|
+
this.h3 = h3 | 0;
|
|
1463
|
+
this.h4 = h4 | 0;
|
|
1464
|
+
}
|
|
1465
|
+
process(view, offset) {
|
|
1466
|
+
for (let i = 0; i < 16; i++, offset += 4)
|
|
1467
|
+
BUF_160[i] = view.getUint32(offset, true);
|
|
1468
|
+
// prettier-ignore
|
|
1469
|
+
let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el;
|
|
1470
|
+
// Instead of iterating 0 to 80, we split it into 5 groups
|
|
1471
|
+
// And use the groups in constants, functions, etc. Much simpler
|
|
1472
|
+
for (let group = 0; group < 5; group++) {
|
|
1473
|
+
const rGroup = 4 - group;
|
|
1474
|
+
const hbl = Kl160[group], hbr = Kr160[group]; // prettier-ignore
|
|
1475
|
+
const rl = idxL[group], rr = idxR[group]; // prettier-ignore
|
|
1476
|
+
const sl = shiftsL160[group], sr = shiftsR160[group]; // prettier-ignore
|
|
1477
|
+
for (let i = 0; i < 16; i++) {
|
|
1478
|
+
const tl = (rotl(al + ripemd_f(group, bl, cl, dl) + BUF_160[rl[i]] + hbl, sl[i]) + el) | 0;
|
|
1479
|
+
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore
|
|
1480
|
+
}
|
|
1481
|
+
// 2 loops are 10% faster
|
|
1482
|
+
for (let i = 0; i < 16; i++) {
|
|
1483
|
+
const tr = (rotl(ar + ripemd_f(rGroup, br, cr, dr) + BUF_160[rr[i]] + hbr, sr[i]) + er) | 0;
|
|
1484
|
+
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
// Add the compressed chunk to the current hash value
|
|
1488
|
+
this.set((this.h1 + cl + dr) | 0, (this.h2 + dl + er) | 0, (this.h3 + el + ar) | 0, (this.h4 + al + br) | 0, (this.h0 + bl + cr) | 0);
|
|
1489
|
+
}
|
|
1490
|
+
roundClean() {
|
|
1491
|
+
clean(BUF_160);
|
|
1492
|
+
}
|
|
1493
|
+
destroy() {
|
|
1494
|
+
this.destroyed = true;
|
|
1495
|
+
clean(this.buffer);
|
|
1496
|
+
this.set(0, 0, 0, 0, 0);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* RIPEMD-160 - a legacy hash function from 1990s.
|
|
1501
|
+
* * https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
|
|
1502
|
+
* * https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf
|
|
1503
|
+
*/
|
|
1504
|
+
const ripemd160 = /* @__PURE__ */ createHasher(() => new _RIPEMD160());
|
|
1505
|
+
|
|
1229
1506
|
/**
|
|
1230
1507
|
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
|
|
1231
1508
|
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
|
|
1232
|
-
* Check out [RFC 4634](https://
|
|
1509
|
+
* Check out [RFC 4634](https://www.rfc-editor.org/rfc/rfc4634) and
|
|
1233
1510
|
* [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
|
|
1234
1511
|
* @module
|
|
1235
1512
|
*/
|
|
@@ -1250,19 +1527,10 @@ const SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|
|
1250
1527
|
]);
|
|
1251
1528
|
/** Reusable temporary buffer. "W" comes straight from spec. */
|
|
1252
1529
|
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|
1253
|
-
|
|
1254
|
-
|
|
1530
|
+
/** Internal 32-byte base SHA2 hash class. */
|
|
1531
|
+
class SHA2_32B extends HashMD {
|
|
1532
|
+
constructor(outputLen) {
|
|
1255
1533
|
super(64, outputLen, 8, false);
|
|
1256
|
-
// We cannot use array here since array allows indexing by variable
|
|
1257
|
-
// which means optimizer/compiler cannot use registers.
|
|
1258
|
-
this.A = SHA256_IV[0] | 0;
|
|
1259
|
-
this.B = SHA256_IV[1] | 0;
|
|
1260
|
-
this.C = SHA256_IV[2] | 0;
|
|
1261
|
-
this.D = SHA256_IV[3] | 0;
|
|
1262
|
-
this.E = SHA256_IV[4] | 0;
|
|
1263
|
-
this.F = SHA256_IV[5] | 0;
|
|
1264
|
-
this.G = SHA256_IV[6] | 0;
|
|
1265
|
-
this.H = SHA256_IV[7] | 0;
|
|
1266
1534
|
}
|
|
1267
1535
|
get() {
|
|
1268
1536
|
const { A, B, C, D, E, F, G, H } = this;
|
|
@@ -1325,99 +1593,104 @@ class SHA256 extends HashMD {
|
|
|
1325
1593
|
clean(this.buffer);
|
|
1326
1594
|
}
|
|
1327
1595
|
}
|
|
1596
|
+
/** Internal SHA2-256 hash class. */
|
|
1597
|
+
class _SHA256 extends SHA2_32B {
|
|
1598
|
+
// We cannot use array here since array allows indexing by variable
|
|
1599
|
+
// which means optimizer/compiler cannot use registers.
|
|
1600
|
+
A = SHA256_IV[0] | 0;
|
|
1601
|
+
B = SHA256_IV[1] | 0;
|
|
1602
|
+
C = SHA256_IV[2] | 0;
|
|
1603
|
+
D = SHA256_IV[3] | 0;
|
|
1604
|
+
E = SHA256_IV[4] | 0;
|
|
1605
|
+
F = SHA256_IV[5] | 0;
|
|
1606
|
+
G = SHA256_IV[6] | 0;
|
|
1607
|
+
H = SHA256_IV[7] | 0;
|
|
1608
|
+
constructor() {
|
|
1609
|
+
super(32);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1328
1612
|
/**
|
|
1329
|
-
* SHA2-256 hash function from RFC 4634.
|
|
1613
|
+
* SHA2-256 hash function from RFC 4634. In JS it's the fastest: even faster than Blake3. Some info:
|
|
1330
1614
|
*
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1615
|
+
* - Trying 2^128 hashes would get 50% chance of collision, using birthday attack.
|
|
1616
|
+
* - BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
|
|
1617
|
+
* - Each sha256 hash is executing 2^18 bit operations.
|
|
1618
|
+
* - Good 2024 ASICs can do 200Th/sec with 3500 watts of power, corresponding to 2^36 hashes/joule.
|
|
1334
1619
|
*/
|
|
1335
|
-
const sha256$1 = /* @__PURE__ */ createHasher(() => new
|
|
1620
|
+
const sha256$1 = /* @__PURE__ */ createHasher(() => new _SHA256(),
|
|
1621
|
+
/* @__PURE__ */ oidNist(0x01));
|
|
1336
1622
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1623
|
+
function hash160(...input) {
|
|
1624
|
+
const buffer = Buff.join(input);
|
|
1625
|
+
const digest = ripemd160(sha256$1(buffer));
|
|
1626
|
+
return new Buff(digest);
|
|
1627
|
+
}
|
|
1628
|
+
function sha256(...input) {
|
|
1629
|
+
const buffer = Buff.join(input);
|
|
1630
|
+
const digest = sha256$1(buffer);
|
|
1631
|
+
return new Buff(digest);
|
|
1632
|
+
}
|
|
1633
|
+
function hash256(...input) {
|
|
1634
|
+
const buffer = Buff.join(input);
|
|
1635
|
+
const digest = sha256$1(sha256$1(buffer));
|
|
1636
|
+
return new Buff(digest);
|
|
1637
|
+
}
|
|
1638
|
+
function hashtag(tag) {
|
|
1639
|
+
const hash = sha256(Buff.str(tag));
|
|
1640
|
+
return Buff.join([hash, hash]);
|
|
1641
|
+
}
|
|
1642
|
+
function hash340(tag, ...input) {
|
|
1643
|
+
const htag = hashtag(tag);
|
|
1644
|
+
const pimg = Buff.join([htag, ...input]);
|
|
1645
|
+
return sha256(pimg);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
var Assert;
|
|
1649
|
+
(function (Assert) {
|
|
1650
|
+
function ok(value, message) {
|
|
1651
|
+
if (!value) {
|
|
1652
|
+
throw new Error(message ?? 'Assertion failed!');
|
|
1653
|
+
}
|
|
1367
1654
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1655
|
+
Assert.ok = ok;
|
|
1656
|
+
function size(input, size, msg) {
|
|
1657
|
+
const bytes = Buff.bytes(input);
|
|
1658
|
+
if (bytes.length !== size) {
|
|
1659
|
+
throw new Error(msg ?? `invalid input size: ${bytes.length} !== ${size}`);
|
|
1660
|
+
}
|
|
1372
1661
|
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
this.oHash.update(out);
|
|
1379
|
-
this.oHash.digestInto(out);
|
|
1380
|
-
this.destroy();
|
|
1662
|
+
Assert.size = size;
|
|
1663
|
+
function is_u8a(value) {
|
|
1664
|
+
if (!(value instanceof Uint8Array)) {
|
|
1665
|
+
throw new TypeError(`invalid Uint8Array: ${String(value)}`);
|
|
1666
|
+
}
|
|
1381
1667
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1668
|
+
Assert.is_u8a = is_u8a;
|
|
1669
|
+
function is_base58(value) {
|
|
1670
|
+
if (typeof value !== 'string' || !/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/.test(value)) {
|
|
1671
|
+
throw new Error('invalid base58 string');
|
|
1672
|
+
}
|
|
1386
1673
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
to.finished = finished;
|
|
1393
|
-
to.destroyed = destroyed;
|
|
1394
|
-
to.blockLen = blockLen;
|
|
1395
|
-
to.outputLen = outputLen;
|
|
1396
|
-
to.oHash = oHash._cloneInto(to.oHash);
|
|
1397
|
-
to.iHash = iHash._cloneInto(to.iHash);
|
|
1398
|
-
return to;
|
|
1674
|
+
Assert.is_base58 = is_base58;
|
|
1675
|
+
function is_base64(value) {
|
|
1676
|
+
if (typeof value !== 'string' || value.length === 0 || !/^[a-zA-Z0-9+/]+={0,2}$/.test(value)) {
|
|
1677
|
+
throw new Error('invalid base64 string');
|
|
1678
|
+
}
|
|
1399
1679
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1680
|
+
Assert.is_base64 = is_base64;
|
|
1681
|
+
function is_b64url(value) {
|
|
1682
|
+
if (typeof value !== 'string' || value.length === 0 || !/^[a-zA-Z0-9\-_]+={0,2}$/.test(value)) {
|
|
1683
|
+
throw new Error('invalid base64url string');
|
|
1684
|
+
}
|
|
1402
1685
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1686
|
+
Assert.is_b64url = is_b64url;
|
|
1687
|
+
function is_bech32(value) {
|
|
1688
|
+
if (typeof value !== 'string' || !/^[a-z]+1[023456789acdefghjklmnpqrstuvwxyz]+$/.test(value)) {
|
|
1689
|
+
throw new Error('invalid bech32 string');
|
|
1690
|
+
}
|
|
1407
1691
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
* HMAC: RFC2104 message authentication code.
|
|
1411
|
-
* @param hash - function that would be used e.g. sha256
|
|
1412
|
-
* @param key - message key
|
|
1413
|
-
* @param message - message data
|
|
1414
|
-
* @example
|
|
1415
|
-
* import { hmac } from '@noble/hashes/hmac';
|
|
1416
|
-
* import { sha256 } from '@noble/hashes/sha2';
|
|
1417
|
-
* const mac1 = hmac(sha256, 'key', 'message');
|
|
1418
|
-
*/
|
|
1419
|
-
const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
|
|
1420
|
-
hmac.create = (hash, key) => new HMAC(hash, key);
|
|
1692
|
+
Assert.is_bech32 = is_bech32;
|
|
1693
|
+
})(Assert || (Assert = {}));
|
|
1421
1694
|
|
|
1422
1695
|
/**
|
|
1423
1696
|
* Hex, bytes and number utilities.
|
|
@@ -1425,32 +1698,26 @@ hmac.create = (hash, key) => new HMAC(hash, key);
|
|
|
1425
1698
|
*/
|
|
1426
1699
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
1427
1700
|
const _0n$6 = /* @__PURE__ */ BigInt(0);
|
|
1428
|
-
const _1n$
|
|
1429
|
-
|
|
1430
|
-
function _abool2(value, title = '') {
|
|
1701
|
+
const _1n$4 = /* @__PURE__ */ BigInt(1);
|
|
1702
|
+
function abool(value, title = '') {
|
|
1431
1703
|
if (typeof value !== 'boolean') {
|
|
1432
|
-
const prefix = title && `"${title}"`;
|
|
1704
|
+
const prefix = title && `"${title}" `;
|
|
1433
1705
|
throw new Error(prefix + 'expected boolean, got type=' + typeof value);
|
|
1434
1706
|
}
|
|
1435
1707
|
return value;
|
|
1436
1708
|
}
|
|
1437
|
-
//
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
const needsLen = length !== undefined;
|
|
1443
|
-
if (!bytes || (needsLen && len !== length)) {
|
|
1444
|
-
const prefix = title && `"${title}" `;
|
|
1445
|
-
const ofLen = needsLen ? ` of length ${length}` : '';
|
|
1446
|
-
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
1447
|
-
throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);
|
|
1709
|
+
// Used in weierstrass, der
|
|
1710
|
+
function abignumber(n) {
|
|
1711
|
+
if (typeof n === 'bigint') {
|
|
1712
|
+
if (!isPosBig(n))
|
|
1713
|
+
throw new Error('positive bigint expected, got ' + n);
|
|
1448
1714
|
}
|
|
1449
|
-
|
|
1715
|
+
else
|
|
1716
|
+
anumber$1(n);
|
|
1717
|
+
return n;
|
|
1450
1718
|
}
|
|
1451
|
-
// Used in weierstrass, der
|
|
1452
1719
|
function numberToHexUnpadded(num) {
|
|
1453
|
-
const hex = num.toString(16);
|
|
1720
|
+
const hex = abignumber(num).toString(16);
|
|
1454
1721
|
return hex.length & 1 ? '0' + hex : hex;
|
|
1455
1722
|
}
|
|
1456
1723
|
function hexToNumber(hex) {
|
|
@@ -1463,56 +1730,40 @@ function bytesToNumberBE(bytes) {
|
|
|
1463
1730
|
return hexToNumber(bytesToHex(bytes));
|
|
1464
1731
|
}
|
|
1465
1732
|
function bytesToNumberLE(bytes) {
|
|
1466
|
-
abytes$1(bytes);
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Takes hex string or Uint8Array, converts to Uint8Array.
|
|
1477
|
-
* Validates output length.
|
|
1478
|
-
* Will throw error for other types.
|
|
1479
|
-
* @param title descriptive title for an error e.g. 'secret key'
|
|
1480
|
-
* @param hex hex string or Uint8Array
|
|
1481
|
-
* @param expectedLength optional, will compare to result array's length
|
|
1482
|
-
* @returns
|
|
1483
|
-
*/
|
|
1484
|
-
function ensureBytes(title, hex, expectedLength) {
|
|
1485
|
-
let res;
|
|
1486
|
-
if (typeof hex === 'string') {
|
|
1487
|
-
try {
|
|
1488
|
-
res = hexToBytes(hex);
|
|
1489
|
-
}
|
|
1490
|
-
catch (e) {
|
|
1491
|
-
throw new Error(title + ' must be hex string or Uint8Array, cause: ' + e);
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
else if (isBytes$1(hex)) {
|
|
1495
|
-
// Uint8Array.from() instead of hash.slice() because node.js Buffer
|
|
1496
|
-
// is instance of Uint8Array, and its slice() creates **mutable** copy
|
|
1497
|
-
res = Uint8Array.from(hex);
|
|
1498
|
-
}
|
|
1499
|
-
else {
|
|
1500
|
-
throw new Error(title + ' must be hex string or Uint8Array');
|
|
1501
|
-
}
|
|
1502
|
-
const len = res.length;
|
|
1503
|
-
if (typeof expectedLength === 'number' && len !== expectedLength)
|
|
1504
|
-
throw new Error(title + ' of length ' + expectedLength + ' expected, got ' + len);
|
|
1733
|
+
return hexToNumber(bytesToHex(copyBytes(abytes$1(bytes)).reverse()));
|
|
1734
|
+
}
|
|
1735
|
+
function numberToBytesBE(n, len) {
|
|
1736
|
+
anumber$1(len);
|
|
1737
|
+
n = abignumber(n);
|
|
1738
|
+
const res = hexToBytes(n.toString(16).padStart(len * 2, '0'));
|
|
1739
|
+
if (res.length !== len)
|
|
1740
|
+
throw new Error('number too large');
|
|
1505
1741
|
return res;
|
|
1506
1742
|
}
|
|
1743
|
+
function numberToBytesLE(n, len) {
|
|
1744
|
+
return numberToBytesBE(n, len).reverse();
|
|
1745
|
+
}
|
|
1507
1746
|
/**
|
|
1508
|
-
*
|
|
1747
|
+
* Copies Uint8Array. We can't use u8a.slice(), because u8a can be Buffer,
|
|
1748
|
+
* and Buffer#slice creates mutable copy. Never use Buffers!
|
|
1509
1749
|
*/
|
|
1510
|
-
|
|
1750
|
+
function copyBytes(bytes) {
|
|
1751
|
+
return Uint8Array.from(bytes);
|
|
1752
|
+
}
|
|
1511
1753
|
/**
|
|
1512
|
-
*
|
|
1513
|
-
*
|
|
1754
|
+
* Decodes 7-bit ASCII string to Uint8Array, throws on non-ascii symbols
|
|
1755
|
+
* Should be safe to use for things expected to be ASCII.
|
|
1756
|
+
* Returns exact same result as `TextEncoder` for ASCII or throws.
|
|
1514
1757
|
*/
|
|
1515
|
-
|
|
1758
|
+
function asciiToBytes(ascii) {
|
|
1759
|
+
return Uint8Array.from(ascii, (c, i) => {
|
|
1760
|
+
const charCode = c.charCodeAt(0);
|
|
1761
|
+
if (c.length !== 1 || charCode > 127) {
|
|
1762
|
+
throw new Error(`string contains non-ASCII character "${ascii[i]}" with code ${charCode} at position ${i}`);
|
|
1763
|
+
}
|
|
1764
|
+
return charCode;
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1516
1767
|
// Is positive bigint
|
|
1517
1768
|
const isPosBig = (n) => typeof n === 'bigint' && _0n$6 <= n;
|
|
1518
1769
|
function inRange(n, min, max) {
|
|
@@ -1540,7 +1791,7 @@ function aInRange(title, n, min, max) {
|
|
|
1540
1791
|
*/
|
|
1541
1792
|
function bitLen(n) {
|
|
1542
1793
|
let len;
|
|
1543
|
-
for (len = 0; n > _0n$6; n >>= _1n$
|
|
1794
|
+
for (len = 0; n > _0n$6; n >>= _1n$4, len += 1)
|
|
1544
1795
|
;
|
|
1545
1796
|
return len;
|
|
1546
1797
|
}
|
|
@@ -1548,7 +1799,7 @@ function bitLen(n) {
|
|
|
1548
1799
|
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.
|
|
1549
1800
|
* Same as BigInt(`0b${Array(i).fill('1').join('')}`)
|
|
1550
1801
|
*/
|
|
1551
|
-
const bitMask = (n) => (_1n$
|
|
1802
|
+
const bitMask = (n) => (_1n$4 << BigInt(n)) - _1n$4;
|
|
1552
1803
|
/**
|
|
1553
1804
|
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
1554
1805
|
* @returns function that will call DRBG until 2nd arg returns something meaningful
|
|
@@ -1557,15 +1808,16 @@ const bitMask = (n) => (_1n$5 << BigInt(n)) - _1n$5;
|
|
|
1557
1808
|
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
|
|
1558
1809
|
*/
|
|
1559
1810
|
function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
if (typeof qByteLen !== 'number' || qByteLen < 2)
|
|
1563
|
-
throw new Error('qByteLen must be a number');
|
|
1811
|
+
anumber$1(hashLen, 'hashLen');
|
|
1812
|
+
anumber$1(qByteLen, 'qByteLen');
|
|
1564
1813
|
if (typeof hmacFn !== 'function')
|
|
1565
1814
|
throw new Error('hmacFn must be a function');
|
|
1566
|
-
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
1567
1815
|
const u8n = (len) => new Uint8Array(len); // creates Uint8Array
|
|
1568
|
-
const
|
|
1816
|
+
const NULL = Uint8Array.of();
|
|
1817
|
+
const byte0 = Uint8Array.of(0x00);
|
|
1818
|
+
const byte1 = Uint8Array.of(0x01);
|
|
1819
|
+
const _maxDrbgIters = 1000;
|
|
1820
|
+
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
|
|
1569
1821
|
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
|
|
1570
1822
|
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
|
|
1571
1823
|
let i = 0; // Iterations counter, will throw when over 1000
|
|
@@ -1574,20 +1826,20 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
1574
1826
|
k.fill(0);
|
|
1575
1827
|
i = 0;
|
|
1576
1828
|
};
|
|
1577
|
-
const h = (...
|
|
1578
|
-
const reseed = (seed =
|
|
1829
|
+
const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs)); // hmac(k)(v, ...values)
|
|
1830
|
+
const reseed = (seed = NULL) => {
|
|
1579
1831
|
// HMAC-DRBG reseed() function. Steps D-G
|
|
1580
|
-
k = h(
|
|
1832
|
+
k = h(byte0, seed); // k = hmac(k || v || 0x00 || seed)
|
|
1581
1833
|
v = h(); // v = hmac(k || v)
|
|
1582
1834
|
if (seed.length === 0)
|
|
1583
1835
|
return;
|
|
1584
|
-
k = h(
|
|
1836
|
+
k = h(byte1, seed); // k = hmac(k || v || 0x01 || seed)
|
|
1585
1837
|
v = h(); // v = hmac(k || v)
|
|
1586
1838
|
};
|
|
1587
1839
|
const gen = () => {
|
|
1588
1840
|
// HMAC-DRBG generate() function
|
|
1589
|
-
if (i++ >=
|
|
1590
|
-
throw new Error('drbg: tried
|
|
1841
|
+
if (i++ >= _maxDrbgIters)
|
|
1842
|
+
throw new Error('drbg: tried max amount of iterations');
|
|
1591
1843
|
let len = 0;
|
|
1592
1844
|
const out = [];
|
|
1593
1845
|
while (len < qByteLen) {
|
|
@@ -1609,7 +1861,7 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|
|
1609
1861
|
};
|
|
1610
1862
|
return genUntil;
|
|
1611
1863
|
}
|
|
1612
|
-
function
|
|
1864
|
+
function validateObject(object, fields = {}, optFields = {}) {
|
|
1613
1865
|
if (!object || typeof object !== 'object')
|
|
1614
1866
|
throw new Error('expected valid options object');
|
|
1615
1867
|
function checkField(fieldName, expectedType, isOpt) {
|
|
@@ -1620,8 +1872,9 @@ function _validateObject(object, fields, optFields = {}) {
|
|
|
1620
1872
|
if (current !== expectedType || val === null)
|
|
1621
1873
|
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
|
|
1622
1874
|
}
|
|
1623
|
-
Object.entries(
|
|
1624
|
-
|
|
1875
|
+
const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));
|
|
1876
|
+
iter(fields, false);
|
|
1877
|
+
iter(optFields, true);
|
|
1625
1878
|
}
|
|
1626
1879
|
/**
|
|
1627
1880
|
* Memoizes (caches) computation result.
|
|
@@ -1646,12 +1899,14 @@ function memoized(fn) {
|
|
|
1646
1899
|
* @module
|
|
1647
1900
|
*/
|
|
1648
1901
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
1902
|
+
// Numbers aren't used in x25519 / x448 builds
|
|
1649
1903
|
// prettier-ignore
|
|
1650
|
-
const _0n$5 =
|
|
1904
|
+
const _0n$5 = /* @__PURE__ */ BigInt(0), _1n$3 = /* @__PURE__ */ BigInt(1), _2n$3 = /* @__PURE__ */ BigInt(2);
|
|
1651
1905
|
// prettier-ignore
|
|
1652
|
-
const
|
|
1906
|
+
const _3n$1 = /* @__PURE__ */ BigInt(3), _4n$1 = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5);
|
|
1653
1907
|
// prettier-ignore
|
|
1654
|
-
const
|
|
1908
|
+
const _7n = /* @__PURE__ */ BigInt(7), _8n = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9);
|
|
1909
|
+
const _16n = /* @__PURE__ */ BigInt(16);
|
|
1655
1910
|
// Calculates a modulo b
|
|
1656
1911
|
function mod(a, b) {
|
|
1657
1912
|
const result = a % b;
|
|
@@ -1679,7 +1934,7 @@ function invert(number, modulo) {
|
|
|
1679
1934
|
let a = mod(number, modulo);
|
|
1680
1935
|
let b = modulo;
|
|
1681
1936
|
// prettier-ignore
|
|
1682
|
-
let x = _0n$5, u = _1n$
|
|
1937
|
+
let x = _0n$5, u = _1n$3;
|
|
1683
1938
|
while (a !== _0n$5) {
|
|
1684
1939
|
// JIT applies optimization if those two lines follow each other
|
|
1685
1940
|
const q = b / a;
|
|
@@ -1689,7 +1944,7 @@ function invert(number, modulo) {
|
|
|
1689
1944
|
b = a, a = r, x = u, u = m;
|
|
1690
1945
|
}
|
|
1691
1946
|
const gcd = b;
|
|
1692
|
-
if (gcd !== _1n$
|
|
1947
|
+
if (gcd !== _1n$3)
|
|
1693
1948
|
throw new Error('invert: does not exist');
|
|
1694
1949
|
return mod(x, modulo);
|
|
1695
1950
|
}
|
|
@@ -1702,7 +1957,7 @@ function assertIsSquare(Fp, root, n) {
|
|
|
1702
1957
|
// n = 72057594037927816n;
|
|
1703
1958
|
// Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
|
|
1704
1959
|
function sqrt3mod4(Fp, n) {
|
|
1705
|
-
const p1div4 = (Fp.ORDER + _1n$
|
|
1960
|
+
const p1div4 = (Fp.ORDER + _1n$3) / _4n$1;
|
|
1706
1961
|
const root = Fp.pow(n, p1div4);
|
|
1707
1962
|
assertIsSquare(Fp, root, n);
|
|
1708
1963
|
return root;
|
|
@@ -1754,7 +2009,7 @@ function tonelliShanks(P) {
|
|
|
1754
2009
|
if (P < _3n$1)
|
|
1755
2010
|
throw new Error('sqrt is not defined for small field');
|
|
1756
2011
|
// Factor P - 1 = Q * 2^S, where Q is odd
|
|
1757
|
-
let Q = P - _1n$
|
|
2012
|
+
let Q = P - _1n$3;
|
|
1758
2013
|
let S = 0;
|
|
1759
2014
|
while (Q % _2n$3 === _0n$5) {
|
|
1760
2015
|
Q /= _2n$3;
|
|
@@ -1775,7 +2030,7 @@ function tonelliShanks(P) {
|
|
|
1775
2030
|
// Slow-path
|
|
1776
2031
|
// TODO: test on Fp2 and others
|
|
1777
2032
|
let cc = _Fp.pow(Z, Q); // c = z^Q
|
|
1778
|
-
const Q1div2 = (Q + _1n$
|
|
2033
|
+
const Q1div2 = (Q + _1n$3) / _2n$3;
|
|
1779
2034
|
return function tonelliSlow(Fp, n) {
|
|
1780
2035
|
if (Fp.is0(n))
|
|
1781
2036
|
return n;
|
|
@@ -1802,7 +2057,7 @@ function tonelliShanks(P) {
|
|
|
1802
2057
|
throw new Error('Cannot find square root');
|
|
1803
2058
|
}
|
|
1804
2059
|
// Calculate the exponent for b: 2^(M - i - 1)
|
|
1805
|
-
const exponent = _1n$
|
|
2060
|
+
const exponent = _1n$3 << BigInt(M - i - 1); // bigint is important
|
|
1806
2061
|
const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
|
|
1807
2062
|
// Update variables
|
|
1808
2063
|
M = i;
|
|
@@ -1846,7 +2101,6 @@ const FIELD_FIELDS = [
|
|
|
1846
2101
|
function validateField(field) {
|
|
1847
2102
|
const initial = {
|
|
1848
2103
|
ORDER: 'bigint',
|
|
1849
|
-
MASK: 'bigint',
|
|
1850
2104
|
BYTES: 'number',
|
|
1851
2105
|
BITS: 'number',
|
|
1852
2106
|
};
|
|
@@ -1854,7 +2108,7 @@ function validateField(field) {
|
|
|
1854
2108
|
map[val] = 'function';
|
|
1855
2109
|
return map;
|
|
1856
2110
|
}, initial);
|
|
1857
|
-
|
|
2111
|
+
validateObject(field, opts);
|
|
1858
2112
|
// const max = 16384;
|
|
1859
2113
|
// if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
|
|
1860
2114
|
// if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
|
|
@@ -1870,15 +2124,15 @@ function FpPow(Fp, num, power) {
|
|
|
1870
2124
|
throw new Error('invalid exponent, negatives unsupported');
|
|
1871
2125
|
if (power === _0n$5)
|
|
1872
2126
|
return Fp.ONE;
|
|
1873
|
-
if (power === _1n$
|
|
2127
|
+
if (power === _1n$3)
|
|
1874
2128
|
return num;
|
|
1875
2129
|
let p = Fp.ONE;
|
|
1876
2130
|
let d = num;
|
|
1877
2131
|
while (power > _0n$5) {
|
|
1878
|
-
if (power & _1n$
|
|
2132
|
+
if (power & _1n$3)
|
|
1879
2133
|
p = Fp.mul(p, d);
|
|
1880
2134
|
d = Fp.sqr(d);
|
|
1881
|
-
power >>= _1n$
|
|
2135
|
+
power >>= _1n$3;
|
|
1882
2136
|
}
|
|
1883
2137
|
return p;
|
|
1884
2138
|
}
|
|
@@ -1919,7 +2173,7 @@ function FpInvertBatch(Fp, nums, passZero = false) {
|
|
|
1919
2173
|
function FpLegendre(Fp, n) {
|
|
1920
2174
|
// We can use 3rd argument as optional cache of this value
|
|
1921
2175
|
// but seems unneeded for now. The operation is very fast.
|
|
1922
|
-
const p1mod2 = (Fp.ORDER - _1n$
|
|
2176
|
+
const p1mod2 = (Fp.ORDER - _1n$3) / _2n$3;
|
|
1923
2177
|
const powered = Fp.pow(n, p1mod2);
|
|
1924
2178
|
const yes = Fp.eql(powered, Fp.ONE);
|
|
1925
2179
|
const zero = Fp.eql(powered, Fp.ZERO);
|
|
@@ -1937,6 +2191,143 @@ function nLength(n, nBitLength) {
|
|
|
1937
2191
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
|
1938
2192
|
return { nBitLength: _nBitLength, nByteLength };
|
|
1939
2193
|
}
|
|
2194
|
+
class _Field {
|
|
2195
|
+
ORDER;
|
|
2196
|
+
BITS;
|
|
2197
|
+
BYTES;
|
|
2198
|
+
isLE;
|
|
2199
|
+
ZERO = _0n$5;
|
|
2200
|
+
ONE = _1n$3;
|
|
2201
|
+
_lengths;
|
|
2202
|
+
_sqrt; // cached sqrt
|
|
2203
|
+
_mod;
|
|
2204
|
+
constructor(ORDER, opts = {}) {
|
|
2205
|
+
if (ORDER <= _0n$5)
|
|
2206
|
+
throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
2207
|
+
let _nbitLength = undefined;
|
|
2208
|
+
this.isLE = false;
|
|
2209
|
+
if (opts != null && typeof opts === 'object') {
|
|
2210
|
+
if (typeof opts.BITS === 'number')
|
|
2211
|
+
_nbitLength = opts.BITS;
|
|
2212
|
+
if (typeof opts.sqrt === 'function')
|
|
2213
|
+
this.sqrt = opts.sqrt;
|
|
2214
|
+
if (typeof opts.isLE === 'boolean')
|
|
2215
|
+
this.isLE = opts.isLE;
|
|
2216
|
+
if (opts.allowedLengths)
|
|
2217
|
+
this._lengths = opts.allowedLengths?.slice();
|
|
2218
|
+
if (typeof opts.modFromBytes === 'boolean')
|
|
2219
|
+
this._mod = opts.modFromBytes;
|
|
2220
|
+
}
|
|
2221
|
+
const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength);
|
|
2222
|
+
if (nByteLength > 2048)
|
|
2223
|
+
throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
2224
|
+
this.ORDER = ORDER;
|
|
2225
|
+
this.BITS = nBitLength;
|
|
2226
|
+
this.BYTES = nByteLength;
|
|
2227
|
+
this._sqrt = undefined;
|
|
2228
|
+
Object.preventExtensions(this);
|
|
2229
|
+
}
|
|
2230
|
+
create(num) {
|
|
2231
|
+
return mod(num, this.ORDER);
|
|
2232
|
+
}
|
|
2233
|
+
isValid(num) {
|
|
2234
|
+
if (typeof num !== 'bigint')
|
|
2235
|
+
throw new Error('invalid field element: expected bigint, got ' + typeof num);
|
|
2236
|
+
return _0n$5 <= num && num < this.ORDER; // 0 is valid element, but it's not invertible
|
|
2237
|
+
}
|
|
2238
|
+
is0(num) {
|
|
2239
|
+
return num === _0n$5;
|
|
2240
|
+
}
|
|
2241
|
+
// is valid and invertible
|
|
2242
|
+
isValidNot0(num) {
|
|
2243
|
+
return !this.is0(num) && this.isValid(num);
|
|
2244
|
+
}
|
|
2245
|
+
isOdd(num) {
|
|
2246
|
+
return (num & _1n$3) === _1n$3;
|
|
2247
|
+
}
|
|
2248
|
+
neg(num) {
|
|
2249
|
+
return mod(-num, this.ORDER);
|
|
2250
|
+
}
|
|
2251
|
+
eql(lhs, rhs) {
|
|
2252
|
+
return lhs === rhs;
|
|
2253
|
+
}
|
|
2254
|
+
sqr(num) {
|
|
2255
|
+
return mod(num * num, this.ORDER);
|
|
2256
|
+
}
|
|
2257
|
+
add(lhs, rhs) {
|
|
2258
|
+
return mod(lhs + rhs, this.ORDER);
|
|
2259
|
+
}
|
|
2260
|
+
sub(lhs, rhs) {
|
|
2261
|
+
return mod(lhs - rhs, this.ORDER);
|
|
2262
|
+
}
|
|
2263
|
+
mul(lhs, rhs) {
|
|
2264
|
+
return mod(lhs * rhs, this.ORDER);
|
|
2265
|
+
}
|
|
2266
|
+
pow(num, power) {
|
|
2267
|
+
return FpPow(this, num, power);
|
|
2268
|
+
}
|
|
2269
|
+
div(lhs, rhs) {
|
|
2270
|
+
return mod(lhs * invert(rhs, this.ORDER), this.ORDER);
|
|
2271
|
+
}
|
|
2272
|
+
// Same as above, but doesn't normalize
|
|
2273
|
+
sqrN(num) {
|
|
2274
|
+
return num * num;
|
|
2275
|
+
}
|
|
2276
|
+
addN(lhs, rhs) {
|
|
2277
|
+
return lhs + rhs;
|
|
2278
|
+
}
|
|
2279
|
+
subN(lhs, rhs) {
|
|
2280
|
+
return lhs - rhs;
|
|
2281
|
+
}
|
|
2282
|
+
mulN(lhs, rhs) {
|
|
2283
|
+
return lhs * rhs;
|
|
2284
|
+
}
|
|
2285
|
+
inv(num) {
|
|
2286
|
+
return invert(num, this.ORDER);
|
|
2287
|
+
}
|
|
2288
|
+
sqrt(num) {
|
|
2289
|
+
// Caching _sqrt speeds up sqrt9mod16 by 5x and tonneli-shanks by 10%
|
|
2290
|
+
if (!this._sqrt)
|
|
2291
|
+
this._sqrt = FpSqrt(this.ORDER);
|
|
2292
|
+
return this._sqrt(this, num);
|
|
2293
|
+
}
|
|
2294
|
+
toBytes(num) {
|
|
2295
|
+
return this.isLE ? numberToBytesLE(num, this.BYTES) : numberToBytesBE(num, this.BYTES);
|
|
2296
|
+
}
|
|
2297
|
+
fromBytes(bytes, skipValidation = false) {
|
|
2298
|
+
abytes$1(bytes);
|
|
2299
|
+
const { _lengths: allowedLengths, BYTES, isLE, ORDER, _mod: modFromBytes } = this;
|
|
2300
|
+
if (allowedLengths) {
|
|
2301
|
+
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
2302
|
+
throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
|
|
2303
|
+
}
|
|
2304
|
+
const padded = new Uint8Array(BYTES);
|
|
2305
|
+
// isLE add 0 to right, !isLE to the left.
|
|
2306
|
+
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
2307
|
+
bytes = padded;
|
|
2308
|
+
}
|
|
2309
|
+
if (bytes.length !== BYTES)
|
|
2310
|
+
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
2311
|
+
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
2312
|
+
if (modFromBytes)
|
|
2313
|
+
scalar = mod(scalar, ORDER);
|
|
2314
|
+
if (!skipValidation)
|
|
2315
|
+
if (!this.isValid(scalar))
|
|
2316
|
+
throw new Error('invalid field element: outside of range 0..ORDER');
|
|
2317
|
+
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
|
2318
|
+
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
|
2319
|
+
return scalar;
|
|
2320
|
+
}
|
|
2321
|
+
// TODO: we don't need it here, move out to separate fn
|
|
2322
|
+
invertBatch(lst) {
|
|
2323
|
+
return FpInvertBatch(this, lst);
|
|
2324
|
+
}
|
|
2325
|
+
// We can't move this out because Fp6, Fp12 implement it
|
|
2326
|
+
// and it's unclear what to return in there.
|
|
2327
|
+
cmov(a, b, condition) {
|
|
2328
|
+
return condition ? b : a;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
1940
2331
|
/**
|
|
1941
2332
|
* Creates a finite field. Major performance optimizations:
|
|
1942
2333
|
* * 1. Denormalized operations like mulN instead of mul.
|
|
@@ -1956,107 +2347,8 @@ function nLength(n, nBitLength) {
|
|
|
1956
2347
|
* @param isLE (default: false) if encoding / decoding should be in little-endian
|
|
1957
2348
|
* @param redef optional faster redefinitions of sqrt and other methods
|
|
1958
2349
|
*/
|
|
1959
|
-
function Field(ORDER,
|
|
1960
|
-
|
|
1961
|
-
if (ORDER <= _0n$5)
|
|
1962
|
-
throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
|
|
1963
|
-
let _nbitLength = undefined;
|
|
1964
|
-
let _sqrt = undefined;
|
|
1965
|
-
let modFromBytes = false;
|
|
1966
|
-
let allowedLengths = undefined;
|
|
1967
|
-
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
|
|
1968
|
-
if (opts.sqrt || isLE)
|
|
1969
|
-
throw new Error('cannot specify opts in two arguments');
|
|
1970
|
-
const _opts = bitLenOrOpts;
|
|
1971
|
-
if (_opts.BITS)
|
|
1972
|
-
_nbitLength = _opts.BITS;
|
|
1973
|
-
if (_opts.sqrt)
|
|
1974
|
-
_sqrt = _opts.sqrt;
|
|
1975
|
-
if (typeof _opts.isLE === 'boolean')
|
|
1976
|
-
isLE = _opts.isLE;
|
|
1977
|
-
if (typeof _opts.modFromBytes === 'boolean')
|
|
1978
|
-
modFromBytes = _opts.modFromBytes;
|
|
1979
|
-
allowedLengths = _opts.allowedLengths;
|
|
1980
|
-
}
|
|
1981
|
-
else {
|
|
1982
|
-
if (typeof bitLenOrOpts === 'number')
|
|
1983
|
-
_nbitLength = bitLenOrOpts;
|
|
1984
|
-
if (opts.sqrt)
|
|
1985
|
-
_sqrt = opts.sqrt;
|
|
1986
|
-
}
|
|
1987
|
-
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
|
|
1988
|
-
if (BYTES > 2048)
|
|
1989
|
-
throw new Error('invalid field: expected ORDER of <= 2048 bytes');
|
|
1990
|
-
let sqrtP; // cached sqrtP
|
|
1991
|
-
const f = Object.freeze({
|
|
1992
|
-
ORDER,
|
|
1993
|
-
isLE,
|
|
1994
|
-
BITS,
|
|
1995
|
-
BYTES,
|
|
1996
|
-
MASK: bitMask(BITS),
|
|
1997
|
-
ZERO: _0n$5,
|
|
1998
|
-
ONE: _1n$4,
|
|
1999
|
-
allowedLengths: allowedLengths,
|
|
2000
|
-
create: (num) => mod(num, ORDER),
|
|
2001
|
-
isValid: (num) => {
|
|
2002
|
-
if (typeof num !== 'bigint')
|
|
2003
|
-
throw new Error('invalid field element: expected bigint, got ' + typeof num);
|
|
2004
|
-
return _0n$5 <= num && num < ORDER; // 0 is valid element, but it's not invertible
|
|
2005
|
-
},
|
|
2006
|
-
is0: (num) => num === _0n$5,
|
|
2007
|
-
// is valid and invertible
|
|
2008
|
-
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
|
|
2009
|
-
isOdd: (num) => (num & _1n$4) === _1n$4,
|
|
2010
|
-
neg: (num) => mod(-num, ORDER),
|
|
2011
|
-
eql: (lhs, rhs) => lhs === rhs,
|
|
2012
|
-
sqr: (num) => mod(num * num, ORDER),
|
|
2013
|
-
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
|
|
2014
|
-
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
|
|
2015
|
-
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
|
|
2016
|
-
pow: (num, power) => FpPow(f, num, power),
|
|
2017
|
-
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
|
|
2018
|
-
// Same as above, but doesn't normalize
|
|
2019
|
-
sqrN: (num) => num * num,
|
|
2020
|
-
addN: (lhs, rhs) => lhs + rhs,
|
|
2021
|
-
subN: (lhs, rhs) => lhs - rhs,
|
|
2022
|
-
mulN: (lhs, rhs) => lhs * rhs,
|
|
2023
|
-
inv: (num) => invert(num, ORDER),
|
|
2024
|
-
sqrt: _sqrt ||
|
|
2025
|
-
((n) => {
|
|
2026
|
-
if (!sqrtP)
|
|
2027
|
-
sqrtP = FpSqrt(ORDER);
|
|
2028
|
-
return sqrtP(f, n);
|
|
2029
|
-
}),
|
|
2030
|
-
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
|
|
2031
|
-
fromBytes: (bytes, skipValidation = true) => {
|
|
2032
|
-
if (allowedLengths) {
|
|
2033
|
-
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|
2034
|
-
throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
|
|
2035
|
-
}
|
|
2036
|
-
const padded = new Uint8Array(BYTES);
|
|
2037
|
-
// isLE add 0 to right, !isLE to the left.
|
|
2038
|
-
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
|
|
2039
|
-
bytes = padded;
|
|
2040
|
-
}
|
|
2041
|
-
if (bytes.length !== BYTES)
|
|
2042
|
-
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
|
|
2043
|
-
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|
2044
|
-
if (modFromBytes)
|
|
2045
|
-
scalar = mod(scalar, ORDER);
|
|
2046
|
-
if (!skipValidation)
|
|
2047
|
-
if (!f.isValid(scalar))
|
|
2048
|
-
throw new Error('invalid field element: outside of range 0..ORDER');
|
|
2049
|
-
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
|
|
2050
|
-
// protocol may allow non-reduced scalar that reduced later or changed some other way.
|
|
2051
|
-
return scalar;
|
|
2052
|
-
},
|
|
2053
|
-
// TODO: we don't need it here, move out to separate fn
|
|
2054
|
-
invertBatch: (lst) => FpInvertBatch(f, lst),
|
|
2055
|
-
// We can't move this out because Fp6, Fp12 implement it
|
|
2056
|
-
// and it's unclear what to return in there.
|
|
2057
|
-
cmov: (a, b, c) => (c ? b : a),
|
|
2058
|
-
});
|
|
2059
|
-
return Object.freeze(f);
|
|
2350
|
+
function Field(ORDER, opts = {}) {
|
|
2351
|
+
return new _Field(ORDER, opts);
|
|
2060
2352
|
}
|
|
2061
2353
|
/**
|
|
2062
2354
|
* Returns total number of bytes consumed by the field element.
|
|
@@ -2090,11 +2382,12 @@ function getMinHashLength(fieldOrder) {
|
|
|
2090
2382
|
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
|
|
2091
2383
|
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
|
|
2092
2384
|
* @param hash hash output from SHA3 or a similar function
|
|
2093
|
-
* @param groupOrder size of subgroup - (e.g. secp256k1.
|
|
2385
|
+
* @param groupOrder size of subgroup - (e.g. secp256k1.Point.Fn.ORDER)
|
|
2094
2386
|
* @param isLE interpret hash bytes as LE num
|
|
2095
2387
|
* @returns valid private scalar
|
|
2096
2388
|
*/
|
|
2097
2389
|
function mapHashToField(key, fieldOrder, isLE = false) {
|
|
2390
|
+
abytes$1(key);
|
|
2098
2391
|
const len = key.length;
|
|
2099
2392
|
const fieldLen = getFieldBytesLength(fieldOrder);
|
|
2100
2393
|
const minLen = getMinHashLength(fieldOrder);
|
|
@@ -2103,7 +2396,7 @@ function mapHashToField(key, fieldOrder, isLE = false) {
|
|
|
2103
2396
|
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
|
|
2104
2397
|
const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
|
|
2105
2398
|
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
|
|
2106
|
-
const reduced = mod(num, fieldOrder - _1n$
|
|
2399
|
+
const reduced = mod(num, fieldOrder - _1n$3) + _1n$3;
|
|
2107
2400
|
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|
2108
2401
|
}
|
|
2109
2402
|
|
|
@@ -2113,8 +2406,8 @@ function mapHashToField(key, fieldOrder, isLE = false) {
|
|
|
2113
2406
|
* @module
|
|
2114
2407
|
*/
|
|
2115
2408
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
2116
|
-
const _0n$4 = BigInt(0);
|
|
2117
|
-
const _1n$
|
|
2409
|
+
const _0n$4 = /* @__PURE__ */ BigInt(0);
|
|
2410
|
+
const _1n$2 = /* @__PURE__ */ BigInt(1);
|
|
2118
2411
|
function negateCt(condition, item) {
|
|
2119
2412
|
const neg = item.negate();
|
|
2120
2413
|
return condition ? neg : item;
|
|
@@ -2154,7 +2447,7 @@ function calcOffsets(n, window, wOpts) {
|
|
|
2154
2447
|
if (wbits > windowSize) {
|
|
2155
2448
|
// we skip zero, which means instead of `>= size-1`, we do `> size`
|
|
2156
2449
|
wbits -= maxNumber; // -32, can be maxNumber - wbits, but then we need to set isNeg here.
|
|
2157
|
-
nextN += _1n$
|
|
2450
|
+
nextN += _1n$2; // +256 (carry)
|
|
2158
2451
|
}
|
|
2159
2452
|
const offsetStart = window * windowSize;
|
|
2160
2453
|
const offset = offsetStart + Math.abs(wbits) - 1; // -1 because we skip zero
|
|
@@ -2164,22 +2457,6 @@ function calcOffsets(n, window, wOpts) {
|
|
|
2164
2457
|
const offsetF = offsetStart; // fake offset for noise
|
|
2165
2458
|
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
|
|
2166
2459
|
}
|
|
2167
|
-
function validateMSMPoints(points, c) {
|
|
2168
|
-
if (!Array.isArray(points))
|
|
2169
|
-
throw new Error('array expected');
|
|
2170
|
-
points.forEach((p, i) => {
|
|
2171
|
-
if (!(p instanceof c))
|
|
2172
|
-
throw new Error('invalid point at index ' + i);
|
|
2173
|
-
});
|
|
2174
|
-
}
|
|
2175
|
-
function validateMSMScalars(scalars, field) {
|
|
2176
|
-
if (!Array.isArray(scalars))
|
|
2177
|
-
throw new Error('array of scalars expected');
|
|
2178
|
-
scalars.forEach((s, i) => {
|
|
2179
|
-
if (!field.isValid(s))
|
|
2180
|
-
throw new Error('invalid scalar at index ' + i);
|
|
2181
|
-
});
|
|
2182
|
-
}
|
|
2183
2460
|
// Since points in different groups cannot be equal (different object constructor),
|
|
2184
2461
|
// we can have single place to store precomputes.
|
|
2185
2462
|
// Allows to make points frozen / immutable.
|
|
@@ -2213,6 +2490,10 @@ function assert0(n) {
|
|
|
2213
2490
|
* This would allow windows to be in different memory locations
|
|
2214
2491
|
*/
|
|
2215
2492
|
class wNAF {
|
|
2493
|
+
BASE;
|
|
2494
|
+
ZERO;
|
|
2495
|
+
Fn;
|
|
2496
|
+
bits;
|
|
2216
2497
|
// Parametrized with a given Point class (not individual point)
|
|
2217
2498
|
constructor(Point, bits) {
|
|
2218
2499
|
this.BASE = Point.BASE;
|
|
@@ -2224,10 +2505,10 @@ class wNAF {
|
|
|
2224
2505
|
_unsafeLadder(elm, n, p = this.ZERO) {
|
|
2225
2506
|
let d = elm;
|
|
2226
2507
|
while (n > _0n$4) {
|
|
2227
|
-
if (n & _1n$
|
|
2508
|
+
if (n & _1n$2)
|
|
2228
2509
|
p = p.add(d);
|
|
2229
2510
|
d = d.double();
|
|
2230
|
-
n >>= _1n$
|
|
2511
|
+
n >>= _1n$2;
|
|
2231
2512
|
}
|
|
2232
2513
|
return p;
|
|
2233
2514
|
}
|
|
@@ -2369,73 +2650,16 @@ function mulEndoUnsafe(Point, point, k1, k2) {
|
|
|
2369
2650
|
let p1 = Point.ZERO;
|
|
2370
2651
|
let p2 = Point.ZERO;
|
|
2371
2652
|
while (k1 > _0n$4 || k2 > _0n$4) {
|
|
2372
|
-
if (k1 & _1n$
|
|
2653
|
+
if (k1 & _1n$2)
|
|
2373
2654
|
p1 = p1.add(acc);
|
|
2374
|
-
if (k2 & _1n$
|
|
2655
|
+
if (k2 & _1n$2)
|
|
2375
2656
|
p2 = p2.add(acc);
|
|
2376
2657
|
acc = acc.double();
|
|
2377
|
-
k1 >>= _1n$
|
|
2378
|
-
k2 >>= _1n$
|
|
2658
|
+
k1 >>= _1n$2;
|
|
2659
|
+
k2 >>= _1n$2;
|
|
2379
2660
|
}
|
|
2380
2661
|
return { p1, p2 };
|
|
2381
2662
|
}
|
|
2382
|
-
/**
|
|
2383
|
-
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
|
|
2384
|
-
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
|
|
2385
|
-
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
|
|
2386
|
-
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
|
|
2387
|
-
* @param c Curve Point constructor
|
|
2388
|
-
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
|
|
2389
|
-
* @param points array of L curve points
|
|
2390
|
-
* @param scalars array of L scalars (aka secret keys / bigints)
|
|
2391
|
-
*/
|
|
2392
|
-
function pippenger(c, fieldN, points, scalars) {
|
|
2393
|
-
// If we split scalars by some window (let's say 8 bits), every chunk will only
|
|
2394
|
-
// take 256 buckets even if there are 4096 scalars, also re-uses double.
|
|
2395
|
-
// TODO:
|
|
2396
|
-
// - https://eprint.iacr.org/2024/750.pdf
|
|
2397
|
-
// - https://tches.iacr.org/index.php/TCHES/article/view/10287
|
|
2398
|
-
// 0 is accepted in scalars
|
|
2399
|
-
validateMSMPoints(points, c);
|
|
2400
|
-
validateMSMScalars(scalars, fieldN);
|
|
2401
|
-
const plength = points.length;
|
|
2402
|
-
const slength = scalars.length;
|
|
2403
|
-
if (plength !== slength)
|
|
2404
|
-
throw new Error('arrays of points and scalars must have equal length');
|
|
2405
|
-
// if (plength === 0) throw new Error('array must be of length >= 2');
|
|
2406
|
-
const zero = c.ZERO;
|
|
2407
|
-
const wbits = bitLen(BigInt(plength));
|
|
2408
|
-
let windowSize = 1; // bits
|
|
2409
|
-
if (wbits > 12)
|
|
2410
|
-
windowSize = wbits - 3;
|
|
2411
|
-
else if (wbits > 4)
|
|
2412
|
-
windowSize = wbits - 2;
|
|
2413
|
-
else if (wbits > 0)
|
|
2414
|
-
windowSize = 2;
|
|
2415
|
-
const MASK = bitMask(windowSize);
|
|
2416
|
-
const buckets = new Array(Number(MASK) + 1).fill(zero); // +1 for zero array
|
|
2417
|
-
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
|
|
2418
|
-
let sum = zero;
|
|
2419
|
-
for (let i = lastBits; i >= 0; i -= windowSize) {
|
|
2420
|
-
buckets.fill(zero);
|
|
2421
|
-
for (let j = 0; j < slength; j++) {
|
|
2422
|
-
const scalar = scalars[j];
|
|
2423
|
-
const wbits = Number((scalar >> BigInt(i)) & MASK);
|
|
2424
|
-
buckets[wbits] = buckets[wbits].add(points[j]);
|
|
2425
|
-
}
|
|
2426
|
-
let resI = zero; // not using this will do small speed-up, but will lose ct
|
|
2427
|
-
// Skip first bucket, because it is zero
|
|
2428
|
-
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
|
|
2429
|
-
sumI = sumI.add(buckets[j]);
|
|
2430
|
-
resI = resI.add(sumI);
|
|
2431
|
-
}
|
|
2432
|
-
sum = sum.add(resI);
|
|
2433
|
-
if (i !== 0)
|
|
2434
|
-
for (let j = 0; j < windowSize; j++)
|
|
2435
|
-
sum = sum.double();
|
|
2436
|
-
}
|
|
2437
|
-
return sum;
|
|
2438
|
-
}
|
|
2439
2663
|
function createField(order, field, isLE) {
|
|
2440
2664
|
if (field) {
|
|
2441
2665
|
if (field.ORDER !== order)
|
|
@@ -2448,7 +2672,7 @@ function createField(order, field, isLE) {
|
|
|
2448
2672
|
}
|
|
2449
2673
|
}
|
|
2450
2674
|
/** Validates CURVE opts and creates fields */
|
|
2451
|
-
function
|
|
2675
|
+
function createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
|
2452
2676
|
if (FpFnLE === undefined)
|
|
2453
2677
|
FpFnLE = type === 'edwards';
|
|
2454
2678
|
if (!CURVE || typeof CURVE !== 'object')
|
|
@@ -2470,6 +2694,12 @@ function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
|
|
2470
2694
|
CURVE = Object.freeze(Object.assign({}, CURVE));
|
|
2471
2695
|
return { CURVE, Fp, Fn };
|
|
2472
2696
|
}
|
|
2697
|
+
function createKeygen(randomSecretKey, getPublicKey) {
|
|
2698
|
+
return function keygen(seed) {
|
|
2699
|
+
const secretKey = randomSecretKey(seed);
|
|
2700
|
+
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
2473
2703
|
|
|
2474
2704
|
/**
|
|
2475
2705
|
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
|
|
@@ -2522,7 +2752,7 @@ function _splitEndoScalar(k, basis, n) {
|
|
|
2522
2752
|
k2 = -k2;
|
|
2523
2753
|
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
|
|
2524
2754
|
// This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
|
|
2525
|
-
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n$
|
|
2755
|
+
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n$1; // Half bits of N
|
|
2526
2756
|
if (k1 < _0n$3 || k1 >= MAX_NUM || k2 < _0n$3 || k2 >= MAX_NUM) {
|
|
2527
2757
|
throw new Error('splitScalar (endomorphism): failed, k=' + k);
|
|
2528
2758
|
}
|
|
@@ -2539,8 +2769,8 @@ function validateSigOpts(opts, def) {
|
|
|
2539
2769
|
// @ts-ignore
|
|
2540
2770
|
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
|
|
2541
2771
|
}
|
|
2542
|
-
|
|
2543
|
-
|
|
2772
|
+
abool(optsn.lowS, 'lowS');
|
|
2773
|
+
abool(optsn.prehash, 'prehash');
|
|
2544
2774
|
if (optsn.format !== undefined)
|
|
2545
2775
|
validateSigFormat(optsn.format);
|
|
2546
2776
|
return optsn;
|
|
@@ -2570,10 +2800,10 @@ const DER = {
|
|
|
2570
2800
|
throw new E('tlv.encode: unpadded data');
|
|
2571
2801
|
const dataLen = data.length / 2;
|
|
2572
2802
|
const len = numberToHexUnpadded(dataLen);
|
|
2573
|
-
if ((len.length / 2) &
|
|
2803
|
+
if ((len.length / 2) & 0b1000_0000)
|
|
2574
2804
|
throw new E('tlv.encode: long form length too big');
|
|
2575
2805
|
// length of length with long form flag
|
|
2576
|
-
const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) |
|
|
2806
|
+
const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) | 0b1000_0000) : '';
|
|
2577
2807
|
const t = numberToHexUnpadded(tag);
|
|
2578
2808
|
return t + lenLen + len + data;
|
|
2579
2809
|
},
|
|
@@ -2586,13 +2816,13 @@ const DER = {
|
|
|
2586
2816
|
if (data.length < 2 || data[pos++] !== tag)
|
|
2587
2817
|
throw new E('tlv.decode: wrong tlv');
|
|
2588
2818
|
const first = data[pos++];
|
|
2589
|
-
const isLong = !!(first &
|
|
2819
|
+
const isLong = !!(first & 0b1000_0000); // First bit of first length byte is flag for short/long form
|
|
2590
2820
|
let length = 0;
|
|
2591
2821
|
if (!isLong)
|
|
2592
2822
|
length = first;
|
|
2593
2823
|
else {
|
|
2594
2824
|
// Long form: [longFlag(1bit), lengthLength(7bit), length (BE)]
|
|
2595
|
-
const lenLen = first &
|
|
2825
|
+
const lenLen = first & 0b0111_1111;
|
|
2596
2826
|
if (!lenLen)
|
|
2597
2827
|
throw new E('tlv.decode(long): indefinite length not supported');
|
|
2598
2828
|
if (lenLen > 4)
|
|
@@ -2633,17 +2863,17 @@ const DER = {
|
|
|
2633
2863
|
},
|
|
2634
2864
|
decode(data) {
|
|
2635
2865
|
const { Err: E } = DER;
|
|
2636
|
-
if (data[0] &
|
|
2866
|
+
if (data[0] & 0b1000_0000)
|
|
2637
2867
|
throw new E('invalid signature integer: negative');
|
|
2638
|
-
if (data[0] === 0x00 && !(data[1] &
|
|
2868
|
+
if (data[0] === 0x00 && !(data[1] & 0b1000_0000))
|
|
2639
2869
|
throw new E('invalid signature integer: unnecessary leading zero');
|
|
2640
2870
|
return bytesToNumberBE(data);
|
|
2641
2871
|
},
|
|
2642
2872
|
},
|
|
2643
|
-
toSig(
|
|
2873
|
+
toSig(bytes) {
|
|
2644
2874
|
// parse DER signature
|
|
2645
2875
|
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|
2646
|
-
const data =
|
|
2876
|
+
const data = abytes$1(bytes, undefined, 'signature');
|
|
2647
2877
|
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(0x30, data);
|
|
2648
2878
|
if (seqLeftBytes.length)
|
|
2649
2879
|
throw new E('invalid signature: left bytes after parsing');
|
|
@@ -2663,56 +2893,38 @@ const DER = {
|
|
|
2663
2893
|
};
|
|
2664
2894
|
// Be friendly to bad ECMAScript parsers by not using bigint literals
|
|
2665
2895
|
// prettier-ignore
|
|
2666
|
-
const _0n$3 = BigInt(0), _1n$
|
|
2667
|
-
function _normFnElement(Fn, key) {
|
|
2668
|
-
const { BYTES: expected } = Fn;
|
|
2669
|
-
let num;
|
|
2670
|
-
if (typeof key === 'bigint') {
|
|
2671
|
-
num = key;
|
|
2672
|
-
}
|
|
2673
|
-
else {
|
|
2674
|
-
let bytes = ensureBytes('private key', key);
|
|
2675
|
-
try {
|
|
2676
|
-
num = Fn.fromBytes(bytes);
|
|
2677
|
-
}
|
|
2678
|
-
catch (error) {
|
|
2679
|
-
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
if (!Fn.isValidNot0(num))
|
|
2683
|
-
throw new Error('invalid private key: out of range [1..N-1]');
|
|
2684
|
-
return num;
|
|
2685
|
-
}
|
|
2896
|
+
const _0n$3 = BigInt(0), _1n$1 = BigInt(1), _2n$2 = BigInt(2), _3n = BigInt(3), _4n = BigInt(4);
|
|
2686
2897
|
/**
|
|
2687
2898
|
* Creates weierstrass Point constructor, based on specified curve options.
|
|
2688
2899
|
*
|
|
2900
|
+
* See {@link WeierstrassOpts}.
|
|
2901
|
+
*
|
|
2689
2902
|
* @example
|
|
2690
2903
|
```js
|
|
2691
2904
|
const opts = {
|
|
2692
|
-
p:
|
|
2693
|
-
n:
|
|
2694
|
-
h:
|
|
2695
|
-
a:
|
|
2696
|
-
b:
|
|
2697
|
-
Gx:
|
|
2698
|
-
Gy:
|
|
2905
|
+
p: 0xfffffffffffffffffffffffffffffffeffffac73n,
|
|
2906
|
+
n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
|
|
2907
|
+
h: 1n,
|
|
2908
|
+
a: 0n,
|
|
2909
|
+
b: 7n,
|
|
2910
|
+
Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
|
|
2911
|
+
Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
|
|
2699
2912
|
};
|
|
2700
|
-
const
|
|
2913
|
+
const secp160k1_Point = weierstrass(opts);
|
|
2701
2914
|
```
|
|
2702
2915
|
*/
|
|
2703
|
-
function
|
|
2704
|
-
const validated =
|
|
2916
|
+
function weierstrass(params, extraOpts = {}) {
|
|
2917
|
+
const validated = createCurveFields('weierstrass', params, extraOpts);
|
|
2705
2918
|
const { Fp, Fn } = validated;
|
|
2706
2919
|
let CURVE = validated.CURVE;
|
|
2707
2920
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|
2708
|
-
|
|
2921
|
+
validateObject(extraOpts, {}, {
|
|
2709
2922
|
allowInfinityPoint: 'boolean',
|
|
2710
2923
|
clearCofactor: 'function',
|
|
2711
2924
|
isTorsionFree: 'function',
|
|
2712
2925
|
fromBytes: 'function',
|
|
2713
2926
|
toBytes: 'function',
|
|
2714
2927
|
endo: 'object',
|
|
2715
|
-
wrapPrivateKey: 'boolean',
|
|
2716
2928
|
});
|
|
2717
2929
|
const { endo } = extraOpts;
|
|
2718
2930
|
if (endo) {
|
|
@@ -2730,7 +2942,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2730
2942
|
function pointToBytes(_c, point, isCompressed) {
|
|
2731
2943
|
const { x, y } = point.toAffine();
|
|
2732
2944
|
const bx = Fp.toBytes(x);
|
|
2733
|
-
|
|
2945
|
+
abool(isCompressed, 'isCompressed');
|
|
2734
2946
|
if (isCompressed) {
|
|
2735
2947
|
assertCompressionIsSupported();
|
|
2736
2948
|
const hasEvenY = !Fp.isOdd(y);
|
|
@@ -2741,7 +2953,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2741
2953
|
}
|
|
2742
2954
|
}
|
|
2743
2955
|
function pointFromBytes(bytes) {
|
|
2744
|
-
|
|
2956
|
+
abytes$1(bytes, undefined, 'Point');
|
|
2745
2957
|
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths; // e.g. for 32-byte: 33, 65
|
|
2746
2958
|
const length = bytes.length;
|
|
2747
2959
|
const head = bytes[0];
|
|
@@ -2761,9 +2973,9 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2761
2973
|
throw new Error('bad point: is not on curve, sqrt error' + err);
|
|
2762
2974
|
}
|
|
2763
2975
|
assertCompressionIsSupported();
|
|
2764
|
-
const
|
|
2765
|
-
const
|
|
2766
|
-
if (
|
|
2976
|
+
const evenY = Fp.isOdd(y);
|
|
2977
|
+
const evenH = (head & 1) === 1; // ECDSA-specific
|
|
2978
|
+
if (evenH !== evenY)
|
|
2767
2979
|
y = Fp.neg(y);
|
|
2768
2980
|
return { x, y };
|
|
2769
2981
|
}
|
|
@@ -2812,7 +3024,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2812
3024
|
}
|
|
2813
3025
|
function aprjpoint(other) {
|
|
2814
3026
|
if (!(other instanceof Point))
|
|
2815
|
-
throw new Error('
|
|
3027
|
+
throw new Error('Weierstrass Point expected');
|
|
2816
3028
|
}
|
|
2817
3029
|
function splitEndoScalarN(k) {
|
|
2818
3030
|
if (!endo || !endo.basises)
|
|
@@ -2875,6 +3087,17 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2875
3087
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
|
2876
3088
|
*/
|
|
2877
3089
|
class Point {
|
|
3090
|
+
// base / generator point
|
|
3091
|
+
static BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
3092
|
+
// zero / infinity / identity point
|
|
3093
|
+
static ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
3094
|
+
// math field
|
|
3095
|
+
static Fp = Fp;
|
|
3096
|
+
// scalar field
|
|
3097
|
+
static Fn = Fn;
|
|
3098
|
+
X;
|
|
3099
|
+
Y;
|
|
3100
|
+
Z;
|
|
2878
3101
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|
2879
3102
|
constructor(X, Y, Z) {
|
|
2880
3103
|
this.X = acoord('x', X);
|
|
@@ -2898,12 +3121,12 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
2898
3121
|
return new Point(x, y, Fp.ONE);
|
|
2899
3122
|
}
|
|
2900
3123
|
static fromBytes(bytes) {
|
|
2901
|
-
const P = Point.fromAffine(decodePoint(
|
|
3124
|
+
const P = Point.fromAffine(decodePoint(abytes$1(bytes, undefined, 'point')));
|
|
2902
3125
|
P.assertValidity();
|
|
2903
3126
|
return P;
|
|
2904
3127
|
}
|
|
2905
3128
|
static fromHex(hex) {
|
|
2906
|
-
return Point.fromBytes(
|
|
3129
|
+
return Point.fromBytes(hexToBytes(hex));
|
|
2907
3130
|
}
|
|
2908
3131
|
get x() {
|
|
2909
3132
|
return this.toAffine().x;
|
|
@@ -3090,11 +3313,13 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3090
3313
|
if (!Fn.isValid(sc))
|
|
3091
3314
|
throw new Error('invalid scalar: out of range'); // 0 is valid
|
|
3092
3315
|
if (sc === _0n$3 || p.is0())
|
|
3093
|
-
return Point.ZERO;
|
|
3094
|
-
if (sc === _1n$
|
|
3095
|
-
return p; //
|
|
3316
|
+
return Point.ZERO; // 0
|
|
3317
|
+
if (sc === _1n$1)
|
|
3318
|
+
return p; // 1
|
|
3096
3319
|
if (wnaf.hasCache(this))
|
|
3097
|
-
return this.multiply(sc);
|
|
3320
|
+
return this.multiply(sc); // precomputes
|
|
3321
|
+
// We don't have method for double scalar multiplication (aP + bQ):
|
|
3322
|
+
// Even with using Strauss-Shamir trick, it's 35% slower than naïve mul+add.
|
|
3098
3323
|
if (endo) {
|
|
3099
3324
|
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|
3100
3325
|
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2); // 30% faster vs wnaf.unsafe
|
|
@@ -3104,10 +3329,6 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3104
3329
|
return wnaf.unsafe(p, sc);
|
|
3105
3330
|
}
|
|
3106
3331
|
}
|
|
3107
|
-
multiplyAndAddUnsafe(Q, a, b) {
|
|
3108
|
-
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
|
|
3109
|
-
return sum.is0() ? undefined : sum;
|
|
3110
|
-
}
|
|
3111
3332
|
/**
|
|
3112
3333
|
* Converts Projective point to affine (x, y) coordinates.
|
|
3113
3334
|
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|
@@ -3121,7 +3342,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3121
3342
|
*/
|
|
3122
3343
|
isTorsionFree() {
|
|
3123
3344
|
const { isTorsionFree } = extraOpts;
|
|
3124
|
-
if (cofactor === _1n$
|
|
3345
|
+
if (cofactor === _1n$1)
|
|
3125
3346
|
return true;
|
|
3126
3347
|
if (isTorsionFree)
|
|
3127
3348
|
return isTorsionFree(Point, this);
|
|
@@ -3129,7 +3350,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3129
3350
|
}
|
|
3130
3351
|
clearCofactor() {
|
|
3131
3352
|
const { clearCofactor } = extraOpts;
|
|
3132
|
-
if (cofactor === _1n$
|
|
3353
|
+
if (cofactor === _1n$1)
|
|
3133
3354
|
return this; // Fast-path
|
|
3134
3355
|
if (clearCofactor)
|
|
3135
3356
|
return clearCofactor(Point, this);
|
|
@@ -3140,7 +3361,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3140
3361
|
return this.multiplyUnsafe(cofactor).is0();
|
|
3141
3362
|
}
|
|
3142
3363
|
toBytes(isCompressed = true) {
|
|
3143
|
-
|
|
3364
|
+
abool(isCompressed, 'isCompressed');
|
|
3144
3365
|
this.assertValidity();
|
|
3145
3366
|
return encodePoint(Point, this, isCompressed);
|
|
3146
3367
|
}
|
|
@@ -3150,40 +3371,7 @@ function weierstrassN(params, extraOpts = {}) {
|
|
|
3150
3371
|
toString() {
|
|
3151
3372
|
return `<Point ${this.is0() ? 'ZERO' : this.toHex()}>`;
|
|
3152
3373
|
}
|
|
3153
|
-
// TODO: remove
|
|
3154
|
-
get px() {
|
|
3155
|
-
return this.X;
|
|
3156
|
-
}
|
|
3157
|
-
get py() {
|
|
3158
|
-
return this.X;
|
|
3159
|
-
}
|
|
3160
|
-
get pz() {
|
|
3161
|
-
return this.Z;
|
|
3162
|
-
}
|
|
3163
|
-
toRawBytes(isCompressed = true) {
|
|
3164
|
-
return this.toBytes(isCompressed);
|
|
3165
|
-
}
|
|
3166
|
-
_setWindowSize(windowSize) {
|
|
3167
|
-
this.precompute(windowSize);
|
|
3168
|
-
}
|
|
3169
|
-
static normalizeZ(points) {
|
|
3170
|
-
return normalizeZ(Point, points);
|
|
3171
|
-
}
|
|
3172
|
-
static msm(points, scalars) {
|
|
3173
|
-
return pippenger(Point, Fn, points, scalars);
|
|
3174
|
-
}
|
|
3175
|
-
static fromPrivateKey(privateKey) {
|
|
3176
|
-
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
|
|
3177
|
-
}
|
|
3178
3374
|
}
|
|
3179
|
-
// base / generator point
|
|
3180
|
-
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|
3181
|
-
// zero / infinity / identity point
|
|
3182
|
-
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO); // 0, 1, 0
|
|
3183
|
-
// math field
|
|
3184
|
-
Point.Fp = Fp;
|
|
3185
|
-
// scalar field
|
|
3186
|
-
Point.Fn = Fn;
|
|
3187
3375
|
const bits = Fn.BITS;
|
|
3188
3376
|
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|
3189
3377
|
Point.BASE.precompute(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
|
|
@@ -3212,7 +3400,8 @@ function ecdh(Point, ecdhOpts = {}) {
|
|
|
3212
3400
|
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
|
|
3213
3401
|
function isValidSecretKey(secretKey) {
|
|
3214
3402
|
try {
|
|
3215
|
-
|
|
3403
|
+
const num = Fn.fromBytes(secretKey);
|
|
3404
|
+
return Fn.isValidNot0(num);
|
|
3216
3405
|
}
|
|
3217
3406
|
catch (error) {
|
|
3218
3407
|
return false;
|
|
@@ -3237,7 +3426,7 @@ function ecdh(Point, ecdhOpts = {}) {
|
|
|
3237
3426
|
* (groupLen + ceil(groupLen / 2)) with modulo bias being negligible.
|
|
3238
3427
|
*/
|
|
3239
3428
|
function randomSecretKey(seed = randomBytes_(lengths.seed)) {
|
|
3240
|
-
return mapHashToField(
|
|
3429
|
+
return mapHashToField(abytes$1(seed, lengths.seed, 'seed'), Fn.ORDER);
|
|
3241
3430
|
}
|
|
3242
3431
|
/**
|
|
3243
3432
|
* Computes public key for a secret key. Checks for validity of the secret key.
|
|
@@ -3245,24 +3434,18 @@ function ecdh(Point, ecdhOpts = {}) {
|
|
|
3245
3434
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
|
3246
3435
|
*/
|
|
3247
3436
|
function getPublicKey(secretKey, isCompressed = true) {
|
|
3248
|
-
return Point.BASE.multiply(
|
|
3249
|
-
}
|
|
3250
|
-
function keygen(seed) {
|
|
3251
|
-
const secretKey = randomSecretKey(seed);
|
|
3252
|
-
return { secretKey, publicKey: getPublicKey(secretKey) };
|
|
3437
|
+
return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
|
|
3253
3438
|
}
|
|
3254
3439
|
/**
|
|
3255
3440
|
* Quick and dirty check for item being public key. Does not validate hex, or being on-curve.
|
|
3256
3441
|
*/
|
|
3257
3442
|
function isProbPub(item) {
|
|
3258
|
-
if (typeof item === 'bigint')
|
|
3259
|
-
return false;
|
|
3260
|
-
if (item instanceof Point)
|
|
3261
|
-
return true;
|
|
3262
3443
|
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|
3263
|
-
if (
|
|
3444
|
+
if (!isBytes$1(item))
|
|
3264
3445
|
return undefined;
|
|
3265
|
-
|
|
3446
|
+
if (('_lengths' in Fn && Fn._lengths) || secretKey === publicKey)
|
|
3447
|
+
return undefined;
|
|
3448
|
+
const l = abytes$1(item, undefined, 'key').length;
|
|
3266
3449
|
return l === publicKey || l === publicKeyUncompressed;
|
|
3267
3450
|
}
|
|
3268
3451
|
/**
|
|
@@ -3278,31 +3461,24 @@ function ecdh(Point, ecdhOpts = {}) {
|
|
|
3278
3461
|
throw new Error('first arg must be private key');
|
|
3279
3462
|
if (isProbPub(publicKeyB) === false)
|
|
3280
3463
|
throw new Error('second arg must be public key');
|
|
3281
|
-
const s =
|
|
3282
|
-
const b = Point.
|
|
3464
|
+
const s = Fn.fromBytes(secretKeyA);
|
|
3465
|
+
const b = Point.fromBytes(publicKeyB); // checks for being on-curve
|
|
3283
3466
|
return b.multiply(s).toBytes(isCompressed);
|
|
3284
3467
|
}
|
|
3285
3468
|
const utils = {
|
|
3286
3469
|
isValidSecretKey,
|
|
3287
3470
|
isValidPublicKey,
|
|
3288
3471
|
randomSecretKey,
|
|
3289
|
-
// TODO: remove
|
|
3290
|
-
isValidPrivateKey: isValidSecretKey,
|
|
3291
|
-
randomPrivateKey: randomSecretKey,
|
|
3292
|
-
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
|
|
3293
|
-
precompute(windowSize = 8, point = Point.BASE) {
|
|
3294
|
-
return point.precompute(windowSize, false);
|
|
3295
|
-
},
|
|
3296
3472
|
};
|
|
3473
|
+
const keygen = createKeygen(randomSecretKey, getPublicKey);
|
|
3297
3474
|
return Object.freeze({ getPublicKey, getSharedSecret, keygen, Point, utils, lengths });
|
|
3298
3475
|
}
|
|
3299
3476
|
/**
|
|
3300
3477
|
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
|
|
3301
|
-
* We need `hash` for 2 features:
|
|
3302
|
-
* 1. Message prehash-ing. NOT used if `sign` / `verify` are called with `prehash: false`
|
|
3303
|
-
* 2. k generation in `sign`, using HMAC-drbg(hash)
|
|
3304
3478
|
*
|
|
3305
|
-
*
|
|
3479
|
+
* @param Point created using {@link weierstrass} function
|
|
3480
|
+
* @param hash used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
|
|
3481
|
+
* @param ecdsaOpts rarely needed, see {@link ECDSAOpts}
|
|
3306
3482
|
*
|
|
3307
3483
|
* @example
|
|
3308
3484
|
* ```js
|
|
@@ -3314,28 +3490,28 @@ function ecdh(Point, ecdhOpts = {}) {
|
|
|
3314
3490
|
*/
|
|
3315
3491
|
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
3316
3492
|
ahash(hash);
|
|
3317
|
-
|
|
3493
|
+
validateObject(ecdsaOpts, {}, {
|
|
3318
3494
|
hmac: 'function',
|
|
3319
3495
|
lowS: 'boolean',
|
|
3320
3496
|
randomBytes: 'function',
|
|
3321
3497
|
bits2int: 'function',
|
|
3322
3498
|
bits2int_modN: 'function',
|
|
3323
3499
|
});
|
|
3500
|
+
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|
3324
3501
|
const randomBytes$1 = ecdsaOpts.randomBytes || randomBytes;
|
|
3325
|
-
const hmac$1 = ecdsaOpts.hmac ||
|
|
3326
|
-
((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
|
|
3502
|
+
const hmac$1 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
|
|
3327
3503
|
const { Fp, Fn } = Point;
|
|
3328
3504
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
3329
3505
|
const { keygen, getPublicKey, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|
3330
3506
|
const defaultSigOpts = {
|
|
3331
|
-
prehash:
|
|
3332
|
-
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS :
|
|
3333
|
-
format:
|
|
3507
|
+
prehash: true,
|
|
3508
|
+
lowS: typeof ecdsaOpts.lowS === 'boolean' ? ecdsaOpts.lowS : true,
|
|
3509
|
+
format: 'compact',
|
|
3334
3510
|
extraEntropy: false,
|
|
3335
3511
|
};
|
|
3336
|
-
const
|
|
3512
|
+
const hasLargeCofactor = CURVE_ORDER * _2n$2 < Fp.ORDER; // Won't CURVE().h > 2n be more effective?
|
|
3337
3513
|
function isBiggerThanHalfOrder(number) {
|
|
3338
|
-
const HALF = CURVE_ORDER >> _1n$
|
|
3514
|
+
const HALF = CURVE_ORDER >> _1n$1;
|
|
3339
3515
|
return number > HALF;
|
|
3340
3516
|
}
|
|
3341
3517
|
function validateRS(title, num) {
|
|
@@ -3343,28 +3519,47 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3343
3519
|
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|
3344
3520
|
return num;
|
|
3345
3521
|
}
|
|
3522
|
+
function assertSmallCofactor() {
|
|
3523
|
+
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
3524
|
+
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
3525
|
+
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
3526
|
+
// However, for cofactor>1, r+n may not get q.x:
|
|
3527
|
+
// r+n*i would need to be done instead where i is unknown.
|
|
3528
|
+
// To easily get i, we either need to:
|
|
3529
|
+
// a. increase amount of valid recid values (4, 5...); OR
|
|
3530
|
+
// b. prohibit non-prime-order signatures (recid > 1).
|
|
3531
|
+
if (hasLargeCofactor)
|
|
3532
|
+
throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
|
|
3533
|
+
}
|
|
3346
3534
|
function validateSigLength(bytes, format) {
|
|
3347
3535
|
validateSigFormat(format);
|
|
3348
3536
|
const size = lengths.signature;
|
|
3349
3537
|
const sizer = format === 'compact' ? size : format === 'recovered' ? size + 1 : undefined;
|
|
3350
|
-
return
|
|
3538
|
+
return abytes$1(bytes, sizer);
|
|
3351
3539
|
}
|
|
3352
3540
|
/**
|
|
3353
3541
|
* ECDSA signature with its (r, s) properties. Supports compact, recovered & DER representations.
|
|
3354
3542
|
*/
|
|
3355
3543
|
class Signature {
|
|
3544
|
+
r;
|
|
3545
|
+
s;
|
|
3546
|
+
recovery;
|
|
3356
3547
|
constructor(r, s, recovery) {
|
|
3357
3548
|
this.r = validateRS('r', r); // r in [1..N-1];
|
|
3358
3549
|
this.s = validateRS('s', s); // s in [1..N-1];
|
|
3359
|
-
if (recovery != null)
|
|
3550
|
+
if (recovery != null) {
|
|
3551
|
+
assertSmallCofactor();
|
|
3552
|
+
if (![0, 1, 2, 3].includes(recovery))
|
|
3553
|
+
throw new Error('invalid recovery id');
|
|
3360
3554
|
this.recovery = recovery;
|
|
3555
|
+
}
|
|
3361
3556
|
Object.freeze(this);
|
|
3362
3557
|
}
|
|
3363
|
-
static fromBytes(bytes, format =
|
|
3558
|
+
static fromBytes(bytes, format = defaultSigOpts.format) {
|
|
3364
3559
|
validateSigLength(bytes, format);
|
|
3365
3560
|
let recid;
|
|
3366
3561
|
if (format === 'der') {
|
|
3367
|
-
const { r, s } = DER.toSig(
|
|
3562
|
+
const { r, s } = DER.toSig(abytes$1(bytes));
|
|
3368
3563
|
return new Signature(r, s);
|
|
3369
3564
|
}
|
|
3370
3565
|
if (format === 'recovered') {
|
|
@@ -3372,7 +3567,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3372
3567
|
format = 'compact';
|
|
3373
3568
|
bytes = bytes.subarray(1);
|
|
3374
3569
|
}
|
|
3375
|
-
const L =
|
|
3570
|
+
const L = lengths.signature / 2;
|
|
3376
3571
|
const r = bytes.subarray(0, L);
|
|
3377
3572
|
const s = bytes.subarray(L, L * 2);
|
|
3378
3573
|
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|
@@ -3380,38 +3575,31 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3380
3575
|
static fromHex(hex, format) {
|
|
3381
3576
|
return this.fromBytes(hexToBytes(hex), format);
|
|
3382
3577
|
}
|
|
3578
|
+
assertRecovery() {
|
|
3579
|
+
const { recovery } = this;
|
|
3580
|
+
if (recovery == null)
|
|
3581
|
+
throw new Error('invalid recovery id: must be present');
|
|
3582
|
+
return recovery;
|
|
3583
|
+
}
|
|
3383
3584
|
addRecoveryBit(recovery) {
|
|
3384
3585
|
return new Signature(this.r, this.s, recovery);
|
|
3385
3586
|
}
|
|
3386
3587
|
recoverPublicKey(messageHash) {
|
|
3387
|
-
const
|
|
3388
|
-
const
|
|
3389
|
-
|
|
3390
|
-
throw new Error('recovery id invalid');
|
|
3391
|
-
// ECDSA recovery is hard for cofactor > 1 curves.
|
|
3392
|
-
// In sign, `r = q.x mod n`, and here we recover q.x from r.
|
|
3393
|
-
// While recovering q.x >= n, we need to add r+n for cofactor=1 curves.
|
|
3394
|
-
// However, for cofactor>1, r+n may not get q.x:
|
|
3395
|
-
// r+n*i would need to be done instead where i is unknown.
|
|
3396
|
-
// To easily get i, we either need to:
|
|
3397
|
-
// a. increase amount of valid recid values (4, 5...); OR
|
|
3398
|
-
// b. prohibit non-prime-order signatures (recid > 1).
|
|
3399
|
-
const hasCofactor = CURVE_ORDER * _2n$2 < FIELD_ORDER;
|
|
3400
|
-
if (hasCofactor && rec > 1)
|
|
3401
|
-
throw new Error('recovery id is ambiguous for h>1 curve');
|
|
3402
|
-
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
|
|
3588
|
+
const { r, s } = this;
|
|
3589
|
+
const recovery = this.assertRecovery();
|
|
3590
|
+
const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;
|
|
3403
3591
|
if (!Fp.isValid(radj))
|
|
3404
|
-
throw new Error('recovery id
|
|
3592
|
+
throw new Error('invalid recovery id: sig.r+curve.n != R.x');
|
|
3405
3593
|
const x = Fp.toBytes(radj);
|
|
3406
|
-
const R = Point.fromBytes(concatBytes(pprefix((
|
|
3594
|
+
const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x));
|
|
3407
3595
|
const ir = Fn.inv(radj); // r^-1
|
|
3408
|
-
const h = bits2int_modN(
|
|
3596
|
+
const h = bits2int_modN(abytes$1(messageHash, undefined, 'msgHash')); // Truncate hash
|
|
3409
3597
|
const u1 = Fn.create(-h * ir); // -hr^-1
|
|
3410
3598
|
const u2 = Fn.create(s * ir); // sr^-1
|
|
3411
3599
|
// (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1). unsafe is fine: there is no private data.
|
|
3412
3600
|
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|
3413
3601
|
if (Q.is0())
|
|
3414
|
-
throw new Error('point at infinify');
|
|
3602
|
+
throw new Error('invalid recovery: point at infinify');
|
|
3415
3603
|
Q.assertValidity();
|
|
3416
3604
|
return Q;
|
|
3417
3605
|
}
|
|
@@ -3419,45 +3607,22 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3419
3607
|
hasHighS() {
|
|
3420
3608
|
return isBiggerThanHalfOrder(this.s);
|
|
3421
3609
|
}
|
|
3422
|
-
toBytes(format =
|
|
3610
|
+
toBytes(format = defaultSigOpts.format) {
|
|
3423
3611
|
validateSigFormat(format);
|
|
3424
3612
|
if (format === 'der')
|
|
3425
3613
|
return hexToBytes(DER.hexFromSig(this));
|
|
3426
|
-
const r =
|
|
3427
|
-
const
|
|
3614
|
+
const { r, s } = this;
|
|
3615
|
+
const rb = Fn.toBytes(r);
|
|
3616
|
+
const sb = Fn.toBytes(s);
|
|
3428
3617
|
if (format === 'recovered') {
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
return concatBytes(Uint8Array.of(this.recovery), r, s);
|
|
3618
|
+
assertSmallCofactor();
|
|
3619
|
+
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
|
|
3432
3620
|
}
|
|
3433
|
-
return concatBytes(
|
|
3621
|
+
return concatBytes(rb, sb);
|
|
3434
3622
|
}
|
|
3435
3623
|
toHex(format) {
|
|
3436
3624
|
return bytesToHex(this.toBytes(format));
|
|
3437
3625
|
}
|
|
3438
|
-
// TODO: remove
|
|
3439
|
-
assertValidity() { }
|
|
3440
|
-
static fromCompact(hex) {
|
|
3441
|
-
return Signature.fromBytes(ensureBytes('sig', hex), 'compact');
|
|
3442
|
-
}
|
|
3443
|
-
static fromDER(hex) {
|
|
3444
|
-
return Signature.fromBytes(ensureBytes('sig', hex), 'der');
|
|
3445
|
-
}
|
|
3446
|
-
normalizeS() {
|
|
3447
|
-
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
|
|
3448
|
-
}
|
|
3449
|
-
toDERRawBytes() {
|
|
3450
|
-
return this.toBytes('der');
|
|
3451
|
-
}
|
|
3452
|
-
toDERHex() {
|
|
3453
|
-
return bytesToHex(this.toBytes('der'));
|
|
3454
|
-
}
|
|
3455
|
-
toCompactRawBytes() {
|
|
3456
|
-
return this.toBytes('compact');
|
|
3457
|
-
}
|
|
3458
|
-
toCompactHex() {
|
|
3459
|
-
return bytesToHex(this.toBytes('compact'));
|
|
3460
|
-
}
|
|
3461
3626
|
}
|
|
3462
3627
|
// RFC6979: ensure ECDSA msg is X bytes and < N. RFC suggests optional truncating via bits2octets.
|
|
3463
3628
|
// FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which matches bits2int.
|
|
@@ -3487,8 +3652,8 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3487
3652
|
return Fn.toBytes(num);
|
|
3488
3653
|
}
|
|
3489
3654
|
function validateMsgAndHash(message, prehash) {
|
|
3490
|
-
|
|
3491
|
-
return prehash ?
|
|
3655
|
+
abytes$1(message, undefined, 'message');
|
|
3656
|
+
return prehash ? abytes$1(hash(message), undefined, 'prehashed message') : message;
|
|
3492
3657
|
}
|
|
3493
3658
|
/**
|
|
3494
3659
|
* Steps A, D of RFC6979 3.2.
|
|
@@ -3498,26 +3663,26 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3498
3663
|
* Warning: we cannot assume here that message has same amount of bytes as curve order,
|
|
3499
3664
|
* this will be invalid at least for P521. Also it can be bigger for P224 + SHA256.
|
|
3500
3665
|
*/
|
|
3501
|
-
function prepSig(message,
|
|
3502
|
-
if (['recovered', 'canonical'].some((k) => k in opts))
|
|
3503
|
-
throw new Error('sign() legacy options not supported');
|
|
3666
|
+
function prepSig(message, secretKey, opts) {
|
|
3504
3667
|
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|
3505
3668
|
message = validateMsgAndHash(message, prehash); // RFC6979 3.2 A: h1 = H(m)
|
|
3506
3669
|
// We can't later call bits2octets, since nested bits2int is broken for curves
|
|
3507
3670
|
// with fnBits % 8 !== 0. Because of that, we unwrap it here as int2octets call.
|
|
3508
3671
|
// const bits2octets = (bits) => int2octets(bits2int_modN(bits))
|
|
3509
3672
|
const h1int = bits2int_modN(message);
|
|
3510
|
-
const d =
|
|
3673
|
+
const d = Fn.fromBytes(secretKey); // validate secret key, convert to bigint
|
|
3674
|
+
if (!Fn.isValidNot0(d))
|
|
3675
|
+
throw new Error('invalid private key');
|
|
3511
3676
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
3512
3677
|
// extraEntropy. RFC6979 3.6: additional k' (optional).
|
|
3513
3678
|
if (extraEntropy != null && extraEntropy !== false) {
|
|
3514
3679
|
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
|
|
3515
3680
|
// gen random bytes OR pass as-is
|
|
3516
3681
|
const e = extraEntropy === true ? randomBytes$1(lengths.secretKey) : extraEntropy;
|
|
3517
|
-
seedArgs.push(
|
|
3682
|
+
seedArgs.push(abytes$1(e, undefined, 'extraEntropy')); // check for being bytes
|
|
3518
3683
|
}
|
|
3519
3684
|
const seed = concatBytes(...seedArgs); // Step D of RFC6979 3.2
|
|
3520
|
-
const m = h1int; //
|
|
3685
|
+
const m = h1int; // no need to call bits2int second time here, it is inside truncateHash!
|
|
3521
3686
|
// Converts signature params into point w r/s, checks result for validity.
|
|
3522
3687
|
// To transform k => Signature:
|
|
3523
3688
|
// q = k⋅G
|
|
@@ -3529,7 +3694,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3529
3694
|
function k2sig(kBytes) {
|
|
3530
3695
|
// RFC 6979 Section 3.2, step 3: k = bits2int(T)
|
|
3531
3696
|
// Important: all mod() calls here must be done over N
|
|
3532
|
-
const k = bits2int(kBytes); //
|
|
3697
|
+
const k = bits2int(kBytes); // Cannot use fields methods, since it is group element
|
|
3533
3698
|
if (!Fn.isValidNot0(k))
|
|
3534
3699
|
return; // Valid scalars (including k) must be in 1..N-1
|
|
3535
3700
|
const ik = Fn.inv(k); // k^-1 mod n
|
|
@@ -3537,16 +3702,16 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3537
3702
|
const r = Fn.create(q.x); // r = q.x mod n
|
|
3538
3703
|
if (r === _0n$3)
|
|
3539
3704
|
return;
|
|
3540
|
-
const s = Fn.create(ik * Fn.create(m + r * d)); //
|
|
3705
|
+
const s = Fn.create(ik * Fn.create(m + r * d)); // s = k^-1(m + rd) mod n
|
|
3541
3706
|
if (s === _0n$3)
|
|
3542
3707
|
return;
|
|
3543
|
-
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n$
|
|
3708
|
+
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n$1); // recovery bit (2 or 3 when q.x>n)
|
|
3544
3709
|
let normS = s;
|
|
3545
3710
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|
3546
|
-
normS = Fn.neg(s); // if lowS was passed, ensure s is always
|
|
3547
|
-
recovery ^= 1;
|
|
3711
|
+
normS = Fn.neg(s); // if lowS was passed, ensure s is always in the bottom half of N
|
|
3712
|
+
recovery ^= 1;
|
|
3548
3713
|
}
|
|
3549
|
-
return new Signature(r, normS,
|
|
3714
|
+
return new Signature(r, normS, hasLargeCofactor ? undefined : recovery);
|
|
3550
3715
|
}
|
|
3551
3716
|
return { seed, k2sig };
|
|
3552
3717
|
}
|
|
@@ -3562,46 +3727,10 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3562
3727
|
* ```
|
|
3563
3728
|
*/
|
|
3564
3729
|
function sign(message, secretKey, opts = {}) {
|
|
3565
|
-
message = ensureBytes('message', message);
|
|
3566
3730
|
const { seed, k2sig } = prepSig(message, secretKey, opts); // Steps A, D of RFC6979 3.2.
|
|
3567
3731
|
const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac$1);
|
|
3568
3732
|
const sig = drbg(seed, k2sig); // Steps B, C, D, E, F, G
|
|
3569
|
-
return sig;
|
|
3570
|
-
}
|
|
3571
|
-
function tryParsingSig(sg) {
|
|
3572
|
-
// Try to deduce format
|
|
3573
|
-
let sig = undefined;
|
|
3574
|
-
const isHex = typeof sg === 'string' || isBytes$1(sg);
|
|
3575
|
-
const isObj = !isHex &&
|
|
3576
|
-
sg !== null &&
|
|
3577
|
-
typeof sg === 'object' &&
|
|
3578
|
-
typeof sg.r === 'bigint' &&
|
|
3579
|
-
typeof sg.s === 'bigint';
|
|
3580
|
-
if (!isHex && !isObj)
|
|
3581
|
-
throw new Error('invalid signature, expected Uint8Array, hex string or Signature instance');
|
|
3582
|
-
if (isObj) {
|
|
3583
|
-
sig = new Signature(sg.r, sg.s);
|
|
3584
|
-
}
|
|
3585
|
-
else if (isHex) {
|
|
3586
|
-
try {
|
|
3587
|
-
sig = Signature.fromBytes(ensureBytes('sig', sg), 'der');
|
|
3588
|
-
}
|
|
3589
|
-
catch (derError) {
|
|
3590
|
-
if (!(derError instanceof DER.Err))
|
|
3591
|
-
throw derError;
|
|
3592
|
-
}
|
|
3593
|
-
if (!sig) {
|
|
3594
|
-
try {
|
|
3595
|
-
sig = Signature.fromBytes(ensureBytes('sig', sg), 'compact');
|
|
3596
|
-
}
|
|
3597
|
-
catch (error) {
|
|
3598
|
-
return false;
|
|
3599
|
-
}
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
if (!sig)
|
|
3603
|
-
return false;
|
|
3604
|
-
return sig;
|
|
3733
|
+
return sig.toBytes(opts.format);
|
|
3605
3734
|
}
|
|
3606
3735
|
/**
|
|
3607
3736
|
* Verifies a signature against message and public key.
|
|
@@ -3618,16 +3747,15 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3618
3747
|
*/
|
|
3619
3748
|
function verify(signature, message, publicKey, opts = {}) {
|
|
3620
3749
|
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|
3621
|
-
publicKey =
|
|
3622
|
-
message = validateMsgAndHash(
|
|
3623
|
-
if (
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
if (sig === false)
|
|
3629
|
-
return false;
|
|
3750
|
+
publicKey = abytes$1(publicKey, undefined, 'publicKey');
|
|
3751
|
+
message = validateMsgAndHash(message, prehash);
|
|
3752
|
+
if (!isBytes$1(signature)) {
|
|
3753
|
+
const end = signature instanceof Signature ? ', use sig.toBytes()' : '';
|
|
3754
|
+
throw new Error('verify expects Uint8Array signature' + end);
|
|
3755
|
+
}
|
|
3756
|
+
validateSigLength(signature, format); // execute this twice because we want loud error
|
|
3630
3757
|
try {
|
|
3758
|
+
const sig = Signature.fromBytes(signature, format);
|
|
3631
3759
|
const P = Point.fromBytes(publicKey);
|
|
3632
3760
|
if (lowS && sig.hasHighS())
|
|
3633
3761
|
return false;
|
|
@@ -3665,73 +3793,6 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
3665
3793
|
hash,
|
|
3666
3794
|
});
|
|
3667
3795
|
}
|
|
3668
|
-
function _weierstrass_legacy_opts_to_new(c) {
|
|
3669
|
-
const CURVE = {
|
|
3670
|
-
a: c.a,
|
|
3671
|
-
b: c.b,
|
|
3672
|
-
p: c.Fp.ORDER,
|
|
3673
|
-
n: c.n,
|
|
3674
|
-
h: c.h,
|
|
3675
|
-
Gx: c.Gx,
|
|
3676
|
-
Gy: c.Gy,
|
|
3677
|
-
};
|
|
3678
|
-
const Fp = c.Fp;
|
|
3679
|
-
let allowedLengths = c.allowedPrivateKeyLengths
|
|
3680
|
-
? Array.from(new Set(c.allowedPrivateKeyLengths.map((l) => Math.ceil(l / 2))))
|
|
3681
|
-
: undefined;
|
|
3682
|
-
const Fn = Field(CURVE.n, {
|
|
3683
|
-
BITS: c.nBitLength,
|
|
3684
|
-
allowedLengths: allowedLengths,
|
|
3685
|
-
modFromBytes: c.wrapPrivateKey,
|
|
3686
|
-
});
|
|
3687
|
-
const curveOpts = {
|
|
3688
|
-
Fp,
|
|
3689
|
-
Fn,
|
|
3690
|
-
allowInfinityPoint: c.allowInfinityPoint,
|
|
3691
|
-
endo: c.endo,
|
|
3692
|
-
isTorsionFree: c.isTorsionFree,
|
|
3693
|
-
clearCofactor: c.clearCofactor,
|
|
3694
|
-
fromBytes: c.fromBytes,
|
|
3695
|
-
toBytes: c.toBytes,
|
|
3696
|
-
};
|
|
3697
|
-
return { CURVE, curveOpts };
|
|
3698
|
-
}
|
|
3699
|
-
function _ecdsa_legacy_opts_to_new(c) {
|
|
3700
|
-
const { CURVE, curveOpts } = _weierstrass_legacy_opts_to_new(c);
|
|
3701
|
-
const ecdsaOpts = {
|
|
3702
|
-
hmac: c.hmac,
|
|
3703
|
-
randomBytes: c.randomBytes,
|
|
3704
|
-
lowS: c.lowS,
|
|
3705
|
-
bits2int: c.bits2int,
|
|
3706
|
-
bits2int_modN: c.bits2int_modN,
|
|
3707
|
-
};
|
|
3708
|
-
return { CURVE, curveOpts, hash: c.hash, ecdsaOpts };
|
|
3709
|
-
}
|
|
3710
|
-
function _ecdsa_new_output_to_legacy(c, _ecdsa) {
|
|
3711
|
-
const Point = _ecdsa.Point;
|
|
3712
|
-
return Object.assign({}, _ecdsa, {
|
|
3713
|
-
ProjectivePoint: Point,
|
|
3714
|
-
CURVE: Object.assign({}, c, nLength(Point.Fn.ORDER, Point.Fn.BITS)),
|
|
3715
|
-
});
|
|
3716
|
-
}
|
|
3717
|
-
// _ecdsa_legacy
|
|
3718
|
-
function weierstrass(c) {
|
|
3719
|
-
const { CURVE, curveOpts, hash, ecdsaOpts } = _ecdsa_legacy_opts_to_new(c);
|
|
3720
|
-
const Point = weierstrassN(CURVE, curveOpts);
|
|
3721
|
-
const signs = ecdsa(Point, hash, ecdsaOpts);
|
|
3722
|
-
return _ecdsa_new_output_to_legacy(c, signs);
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
/**
|
|
3726
|
-
* Utilities for short weierstrass curves, combined with noble-hashes.
|
|
3727
|
-
* @module
|
|
3728
|
-
*/
|
|
3729
|
-
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
3730
|
-
/** @deprecated use new `weierstrass()` and `ecdsa()` methods */
|
|
3731
|
-
function createCurve(curveDef, defHash) {
|
|
3732
|
-
const create = (hash) => weierstrass({ ...curveDef, hash: hash });
|
|
3733
|
-
return { ...create(defHash), create };
|
|
3734
|
-
}
|
|
3735
3796
|
|
|
3736
3797
|
/**
|
|
3737
3798
|
* SECG secp256k1. See [pdf](https://www.secg.org/sec2-v2.pdf).
|
|
@@ -3742,7 +3803,7 @@ function createCurve(curveDef, defHash) {
|
|
|
3742
3803
|
*/
|
|
3743
3804
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
3744
3805
|
// Seems like generator was produced from some seed:
|
|
3745
|
-
// `
|
|
3806
|
+
// `Pointk1.BASE.multiply(Pointk1.Fn.inv(2n, N)).toAffine().x`
|
|
3746
3807
|
// // gives short x 0x3b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63n
|
|
3747
3808
|
const secp256k1_CURVE = {
|
|
3748
3809
|
p: BigInt('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'),
|
|
@@ -3761,7 +3822,6 @@ const secp256k1_ENDO = {
|
|
|
3761
3822
|
],
|
|
3762
3823
|
};
|
|
3763
3824
|
const _0n$2 = /* @__PURE__ */ BigInt(0);
|
|
3764
|
-
const _1n$1 = /* @__PURE__ */ BigInt(1);
|
|
3765
3825
|
const _2n$1 = /* @__PURE__ */ BigInt(2);
|
|
3766
3826
|
/**
|
|
3767
3827
|
* √n = n^((p+1)/4) for fields p = 3 mod 4. We unwrap the loop and multiply bit-by-bit.
|
|
@@ -3792,21 +3852,28 @@ function sqrtMod(y) {
|
|
|
3792
3852
|
return root;
|
|
3793
3853
|
}
|
|
3794
3854
|
const Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod });
|
|
3855
|
+
const Pointk1 = /* @__PURE__ */ weierstrass(secp256k1_CURVE, {
|
|
3856
|
+
Fp: Fpk1,
|
|
3857
|
+
endo: secp256k1_ENDO,
|
|
3858
|
+
});
|
|
3795
3859
|
/**
|
|
3796
|
-
* secp256k1 curve
|
|
3860
|
+
* secp256k1 curve: ECDSA and ECDH methods.
|
|
3797
3861
|
*
|
|
3798
|
-
*
|
|
3862
|
+
* Uses sha256 to hash messages. To use a different hash,
|
|
3863
|
+
* pass `{ prehash: false }` to sign / verify.
|
|
3799
3864
|
*
|
|
3800
3865
|
* @example
|
|
3801
3866
|
* ```js
|
|
3802
|
-
* import { secp256k1 } from '@noble/curves/secp256k1';
|
|
3867
|
+
* import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
3803
3868
|
* const { secretKey, publicKey } = secp256k1.keygen();
|
|
3804
|
-
* const
|
|
3869
|
+
* // const publicKey = secp256k1.getPublicKey(secretKey);
|
|
3870
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
3805
3871
|
* const sig = secp256k1.sign(msg, secretKey);
|
|
3806
|
-
* const isValid = secp256k1.verify(sig, msg, publicKey)
|
|
3872
|
+
* const isValid = secp256k1.verify(sig, msg, publicKey);
|
|
3873
|
+
* // const sigKeccak = secp256k1.sign(keccak256(msg), secretKey, { prehash: false });
|
|
3807
3874
|
* ```
|
|
3808
3875
|
*/
|
|
3809
|
-
const secp256k1 =
|
|
3876
|
+
const secp256k1 = /* @__PURE__ */ ecdsa(Pointk1, sha256$1);
|
|
3810
3877
|
// Schnorr signatures are superior to ECDSA from above. Below is Schnorr-specific BIP0340 code.
|
|
3811
3878
|
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
3812
3879
|
/** An object mapping tags to their tagged hash prefix of [SHA256(tag) | SHA256(tag)] */
|
|
@@ -3814,7 +3881,7 @@ const TAGGED_HASH_PREFIXES = {};
|
|
|
3814
3881
|
function taggedHash(tag, ...messages) {
|
|
3815
3882
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|
3816
3883
|
if (tagP === undefined) {
|
|
3817
|
-
const tagH = sha256$1(
|
|
3884
|
+
const tagH = sha256$1(asciiToBytes(tag));
|
|
3818
3885
|
tagP = concatBytes(tagH, tagH);
|
|
3819
3886
|
TAGGED_HASH_PREFIXES[tag] = tagP;
|
|
3820
3887
|
}
|
|
@@ -3822,12 +3889,11 @@ function taggedHash(tag, ...messages) {
|
|
|
3822
3889
|
}
|
|
3823
3890
|
// ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
|
|
3824
3891
|
const pointToBytes = (point) => point.toBytes(true).slice(1);
|
|
3825
|
-
const Pointk1 = /* @__PURE__ */ (() => secp256k1.Point)();
|
|
3826
3892
|
const hasEven = (y) => y % _2n$1 === _0n$2;
|
|
3827
3893
|
// Calculate point, scalar and bytes
|
|
3828
3894
|
function schnorrGetExtPubKey(priv) {
|
|
3829
3895
|
const { Fn, BASE } = Pointk1;
|
|
3830
|
-
const d_ =
|
|
3896
|
+
const d_ = Fn.fromBytes(priv);
|
|
3831
3897
|
const p = BASE.multiply(d_); // P = d'⋅G; 0 < d' < n check is done inside
|
|
3832
3898
|
const scalar = hasEven(p.y) ? d_ : Fn.neg(d_);
|
|
3833
3899
|
return { scalar, bytes: pointToBytes(p) };
|
|
@@ -3870,9 +3936,9 @@ function schnorrGetPublicKey(secretKey) {
|
|
|
3870
3936
|
*/
|
|
3871
3937
|
function schnorrSign(message, secretKey, auxRand = randomBytes(32)) {
|
|
3872
3938
|
const { Fn } = Pointk1;
|
|
3873
|
-
const m =
|
|
3939
|
+
const m = abytes$1(message, undefined, 'message');
|
|
3874
3940
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(secretKey); // checks for isWithinCurveOrder
|
|
3875
|
-
const a =
|
|
3941
|
+
const a = abytes$1(auxRand, 32, 'auxRand'); // Auxiliary random data a: a 32-byte array
|
|
3876
3942
|
const t = Fn.toBytes(d ^ num$1(taggedHash('BIP0340/aux', a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
|
|
3877
3943
|
const rand = taggedHash('BIP0340/nonce', t, px, m); // Let rand = hash/nonce(t || bytes(P) || m)
|
|
3878
3944
|
// Let k' = int(rand) mod n. Fail if k' = 0. Let R = k'⋅G
|
|
@@ -3891,20 +3957,19 @@ function schnorrSign(message, secretKey, auxRand = randomBytes(32)) {
|
|
|
3891
3957
|
* Will swallow errors & return false except for initial type validation of arguments.
|
|
3892
3958
|
*/
|
|
3893
3959
|
function schnorrVerify(signature, message, publicKey) {
|
|
3894
|
-
const { Fn, BASE } = Pointk1;
|
|
3895
|
-
const sig =
|
|
3896
|
-
const m =
|
|
3897
|
-
const pub =
|
|
3960
|
+
const { Fp, Fn, BASE } = Pointk1;
|
|
3961
|
+
const sig = abytes$1(signature, 64, 'signature');
|
|
3962
|
+
const m = abytes$1(message, undefined, 'message');
|
|
3963
|
+
const pub = abytes$1(publicKey, 32, 'publicKey');
|
|
3898
3964
|
try {
|
|
3899
3965
|
const P = lift_x(num$1(pub)); // P = lift_x(int(pk)); fail if that fails
|
|
3900
3966
|
const r = num$1(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
|
3901
|
-
if (!
|
|
3967
|
+
if (!Fp.isValidNot0(r))
|
|
3902
3968
|
return false;
|
|
3903
3969
|
const s = num$1(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
|
3904
|
-
if (!
|
|
3970
|
+
if (!Fn.isValidNot0(s))
|
|
3905
3971
|
return false;
|
|
3906
|
-
// int(challenge(bytes(r)||bytes(P)||m))%n
|
|
3907
|
-
const e = challenge(Fn.toBytes(r), pointToBytes(P), m);
|
|
3972
|
+
const e = challenge(Fn.toBytes(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m))%n
|
|
3908
3973
|
// R = s⋅G - e⋅P, where -eP == (n-e)P
|
|
3909
3974
|
const R = BASE.multiplyUnsafe(s).add(P.multiplyUnsafe(Fn.neg(e)));
|
|
3910
3975
|
const { x, y } = R.toAffine();
|
|
@@ -3922,7 +3987,7 @@ function schnorrVerify(signature, message, publicKey) {
|
|
|
3922
3987
|
* https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
|
|
3923
3988
|
* @example
|
|
3924
3989
|
* ```js
|
|
3925
|
-
* import { schnorr } from '@noble/curves/secp256k1';
|
|
3990
|
+
* import { schnorr } from '@noble/curves/secp256k1.js';
|
|
3926
3991
|
* const { secretKey, publicKey } = schnorr.keygen();
|
|
3927
3992
|
* // const publicKey = schnorr.getPublicKey(secretKey);
|
|
3928
3993
|
* const msg = new TextEncoder().encode('hello');
|
|
@@ -3936,26 +4001,17 @@ const schnorr = /* @__PURE__ */ (() => {
|
|
|
3936
4001
|
const randomSecretKey = (seed = randomBytes(seedLength)) => {
|
|
3937
4002
|
return mapHashToField(seed, secp256k1_CURVE.n);
|
|
3938
4003
|
};
|
|
3939
|
-
function keygen(seed) {
|
|
3940
|
-
const secretKey = randomSecretKey(seed);
|
|
3941
|
-
return { secretKey, publicKey: schnorrGetPublicKey(secretKey) };
|
|
3942
|
-
}
|
|
3943
4004
|
return {
|
|
3944
|
-
keygen,
|
|
4005
|
+
keygen: createKeygen(randomSecretKey, schnorrGetPublicKey),
|
|
3945
4006
|
getPublicKey: schnorrGetPublicKey,
|
|
3946
4007
|
sign: schnorrSign,
|
|
3947
4008
|
verify: schnorrVerify,
|
|
3948
4009
|
Point: Pointk1,
|
|
3949
4010
|
utils: {
|
|
3950
|
-
randomSecretKey
|
|
3951
|
-
randomPrivateKey: randomSecretKey,
|
|
4011
|
+
randomSecretKey,
|
|
3952
4012
|
taggedHash,
|
|
3953
|
-
// TODO: remove
|
|
3954
4013
|
lift_x,
|
|
3955
4014
|
pointToBytes,
|
|
3956
|
-
numberToBytesBE,
|
|
3957
|
-
bytesToNumberBE,
|
|
3958
|
-
mod,
|
|
3959
4015
|
},
|
|
3960
4016
|
lengths: {
|
|
3961
4017
|
secretKey: size,
|
|
@@ -3969,8 +4025,8 @@ const schnorr = /* @__PURE__ */ (() => {
|
|
|
3969
4025
|
|
|
3970
4026
|
const _0n$1 = BigInt(0);
|
|
3971
4027
|
const _2n = BigInt(2);
|
|
3972
|
-
const _N = secp256k1.
|
|
3973
|
-
Field(_N,
|
|
4028
|
+
const _N = secp256k1.Point.Fn.ORDER;
|
|
4029
|
+
Field(_N, { isLE: true });
|
|
3974
4030
|
const GP = secp256k1.Point.BASE;
|
|
3975
4031
|
function tweak_pubkey(pubkey, tweak, format, even_y = false) {
|
|
3976
4032
|
const twk_big = serialize_bytes(tweak).big;
|
|
@@ -3985,19 +4041,21 @@ function tweak_pubkey(pubkey, tweak, format, even_y = false) {
|
|
|
3985
4041
|
}
|
|
3986
4042
|
function sign_ecdsa(seckey, message) {
|
|
3987
4043
|
const msg = serialize_bytes(message);
|
|
3988
|
-
const
|
|
4044
|
+
const sk = serialize_bytes(seckey);
|
|
4045
|
+
const sig = secp256k1.sign(msg, sk, { format: 'der', prehash: false });
|
|
3989
4046
|
return Buff.bytes(sig);
|
|
3990
4047
|
}
|
|
3991
4048
|
function sign_bip340(seckey, message) {
|
|
3992
4049
|
const msg = serialize_bytes(message);
|
|
3993
|
-
const
|
|
3994
|
-
|
|
4050
|
+
const sk = serialize_bytes(seckey);
|
|
4051
|
+
const sig = schnorr.sign(msg, sk);
|
|
4052
|
+
return Buff.bytes(sig);
|
|
3995
4053
|
}
|
|
3996
4054
|
function verify_ecdsa(signature, message, pubkey) {
|
|
3997
4055
|
const sig = serialize_bytes(signature);
|
|
3998
4056
|
const msg = serialize_bytes(message);
|
|
3999
4057
|
const pk = serialize_pubkey(pubkey, 'ecdsa');
|
|
4000
|
-
return secp256k1.verify(sig, msg, pk);
|
|
4058
|
+
return secp256k1.verify(sig, msg, pk, { format: 'der', prehash: false });
|
|
4001
4059
|
}
|
|
4002
4060
|
function verify_bip340(signature, message, pubkey) {
|
|
4003
4061
|
const sig = serialize_bytes(signature);
|
|
@@ -4008,10 +4066,10 @@ function verify_bip340(signature, message, pubkey) {
|
|
|
4008
4066
|
function lift_point(pubkey) {
|
|
4009
4067
|
try {
|
|
4010
4068
|
const pk = serialize_pubkey(pubkey, 'ecdsa');
|
|
4011
|
-
return secp256k1.Point.fromHex(pk);
|
|
4069
|
+
return secp256k1.Point.fromHex(pk.hex);
|
|
4012
4070
|
}
|
|
4013
|
-
catch
|
|
4014
|
-
throw new Error(
|
|
4071
|
+
catch {
|
|
4072
|
+
throw new Error(`invalid pubkey: ${pubkey}`);
|
|
4015
4073
|
}
|
|
4016
4074
|
}
|
|
4017
4075
|
function serialize_pubkey(pubkey, format) {
|
|
@@ -4028,161 +4086,21 @@ function serialize_pubkey(pubkey, format) {
|
|
|
4028
4086
|
return pk;
|
|
4029
4087
|
}
|
|
4030
4088
|
}
|
|
4031
|
-
catch
|
|
4032
|
-
throw new Error(
|
|
4089
|
+
catch {
|
|
4090
|
+
throw new Error(`invalid pubkey: ${String(pubkey)}`);
|
|
4033
4091
|
}
|
|
4034
4092
|
}
|
|
4035
4093
|
function serialize_bytes(bytes) {
|
|
4036
|
-
|
|
4094
|
+
if (bytes instanceof Uint8Array) {
|
|
4037
4095
|
return Buff.bytes(bytes);
|
|
4038
4096
|
}
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
}
|
|
4043
|
-
|
|
4044
|
-
/**
|
|
4045
|
-
|
|
4046
|
-
SHA1 (RFC 3174), MD5 (RFC 1321) and RIPEMD160 (RFC 2286) legacy, weak hash functions.
|
|
4047
|
-
Don't use them in a new protocol. What "weak" means:
|
|
4048
|
-
|
|
4049
|
-
- Collisions can be made with 2^18 effort in MD5, 2^60 in SHA1, 2^80 in RIPEMD160.
|
|
4050
|
-
- No practical pre-image attacks (only theoretical, 2^123.4)
|
|
4051
|
-
- HMAC seems kinda ok: https://datatracker.ietf.org/doc/html/rfc6151
|
|
4052
|
-
* @module
|
|
4053
|
-
*/
|
|
4054
|
-
// RIPEMD-160
|
|
4055
|
-
const Rho160 = /* @__PURE__ */ Uint8Array.from([
|
|
4056
|
-
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
|
|
4057
|
-
]);
|
|
4058
|
-
const Id160 = /* @__PURE__ */ (() => Uint8Array.from(new Array(16).fill(0).map((_, i) => i)))();
|
|
4059
|
-
const Pi160 = /* @__PURE__ */ (() => Id160.map((i) => (9 * i + 5) % 16))();
|
|
4060
|
-
const idxLR = /* @__PURE__ */ (() => {
|
|
4061
|
-
const L = [Id160];
|
|
4062
|
-
const R = [Pi160];
|
|
4063
|
-
const res = [L, R];
|
|
4064
|
-
for (let i = 0; i < 4; i++)
|
|
4065
|
-
for (let j of res)
|
|
4066
|
-
j.push(j[i].map((k) => Rho160[k]));
|
|
4067
|
-
return res;
|
|
4068
|
-
})();
|
|
4069
|
-
const idxL = /* @__PURE__ */ (() => idxLR[0])();
|
|
4070
|
-
const idxR = /* @__PURE__ */ (() => idxLR[1])();
|
|
4071
|
-
// const [idxL, idxR] = idxLR;
|
|
4072
|
-
const shifts160 = /* @__PURE__ */ [
|
|
4073
|
-
[11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8],
|
|
4074
|
-
[12, 13, 11, 15, 6, 9, 9, 7, 12, 15, 11, 13, 7, 8, 7, 7],
|
|
4075
|
-
[13, 15, 14, 11, 7, 7, 6, 8, 13, 14, 13, 12, 5, 5, 6, 9],
|
|
4076
|
-
[14, 11, 12, 14, 8, 6, 5, 5, 15, 12, 15, 14, 9, 9, 8, 6],
|
|
4077
|
-
[15, 12, 13, 13, 9, 5, 8, 6, 14, 11, 12, 11, 8, 6, 5, 5],
|
|
4078
|
-
].map((i) => Uint8Array.from(i));
|
|
4079
|
-
const shiftsL160 = /* @__PURE__ */ idxL.map((idx, i) => idx.map((j) => shifts160[i][j]));
|
|
4080
|
-
const shiftsR160 = /* @__PURE__ */ idxR.map((idx, i) => idx.map((j) => shifts160[i][j]));
|
|
4081
|
-
const Kl160 = /* @__PURE__ */ Uint32Array.from([
|
|
4082
|
-
0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e,
|
|
4083
|
-
]);
|
|
4084
|
-
const Kr160 = /* @__PURE__ */ Uint32Array.from([
|
|
4085
|
-
0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000,
|
|
4086
|
-
]);
|
|
4087
|
-
// It's called f() in spec.
|
|
4088
|
-
function ripemd_f(group, x, y, z) {
|
|
4089
|
-
if (group === 0)
|
|
4090
|
-
return x ^ y ^ z;
|
|
4091
|
-
if (group === 1)
|
|
4092
|
-
return (x & y) | (~x & z);
|
|
4093
|
-
if (group === 2)
|
|
4094
|
-
return (x | ~y) ^ z;
|
|
4095
|
-
if (group === 3)
|
|
4096
|
-
return (x & z) | (y & ~z);
|
|
4097
|
-
return x ^ (y | ~z);
|
|
4098
|
-
}
|
|
4099
|
-
// Reusable temporary buffer
|
|
4100
|
-
const BUF_160 = /* @__PURE__ */ new Uint32Array(16);
|
|
4101
|
-
class RIPEMD160 extends HashMD {
|
|
4102
|
-
constructor() {
|
|
4103
|
-
super(64, 20, 8, true);
|
|
4104
|
-
this.h0 = 0x67452301 | 0;
|
|
4105
|
-
this.h1 = 0xefcdab89 | 0;
|
|
4106
|
-
this.h2 = 0x98badcfe | 0;
|
|
4107
|
-
this.h3 = 0x10325476 | 0;
|
|
4108
|
-
this.h4 = 0xc3d2e1f0 | 0;
|
|
4109
|
-
}
|
|
4110
|
-
get() {
|
|
4111
|
-
const { h0, h1, h2, h3, h4 } = this;
|
|
4112
|
-
return [h0, h1, h2, h3, h4];
|
|
4113
|
-
}
|
|
4114
|
-
set(h0, h1, h2, h3, h4) {
|
|
4115
|
-
this.h0 = h0 | 0;
|
|
4116
|
-
this.h1 = h1 | 0;
|
|
4117
|
-
this.h2 = h2 | 0;
|
|
4118
|
-
this.h3 = h3 | 0;
|
|
4119
|
-
this.h4 = h4 | 0;
|
|
4120
|
-
}
|
|
4121
|
-
process(view, offset) {
|
|
4122
|
-
for (let i = 0; i < 16; i++, offset += 4)
|
|
4123
|
-
BUF_160[i] = view.getUint32(offset, true);
|
|
4124
|
-
// prettier-ignore
|
|
4125
|
-
let al = this.h0 | 0, ar = al, bl = this.h1 | 0, br = bl, cl = this.h2 | 0, cr = cl, dl = this.h3 | 0, dr = dl, el = this.h4 | 0, er = el;
|
|
4126
|
-
// Instead of iterating 0 to 80, we split it into 5 groups
|
|
4127
|
-
// And use the groups in constants, functions, etc. Much simpler
|
|
4128
|
-
for (let group = 0; group < 5; group++) {
|
|
4129
|
-
const rGroup = 4 - group;
|
|
4130
|
-
const hbl = Kl160[group], hbr = Kr160[group]; // prettier-ignore
|
|
4131
|
-
const rl = idxL[group], rr = idxR[group]; // prettier-ignore
|
|
4132
|
-
const sl = shiftsL160[group], sr = shiftsR160[group]; // prettier-ignore
|
|
4133
|
-
for (let i = 0; i < 16; i++) {
|
|
4134
|
-
const tl = (rotl(al + ripemd_f(group, bl, cl, dl) + BUF_160[rl[i]] + hbl, sl[i]) + el) | 0;
|
|
4135
|
-
al = el, el = dl, dl = rotl(cl, 10) | 0, cl = bl, bl = tl; // prettier-ignore
|
|
4136
|
-
}
|
|
4137
|
-
// 2 loops are 10% faster
|
|
4138
|
-
for (let i = 0; i < 16; i++) {
|
|
4139
|
-
const tr = (rotl(ar + ripemd_f(rGroup, br, cr, dr) + BUF_160[rr[i]] + hbr, sr[i]) + er) | 0;
|
|
4140
|
-
ar = er, er = dr, dr = rotl(cr, 10) | 0, cr = br, br = tr; // prettier-ignore
|
|
4141
|
-
}
|
|
4097
|
+
if (typeof bytes === 'string') {
|
|
4098
|
+
if (bytes.length > 0 && bytes.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(bytes)) {
|
|
4099
|
+
return Buff.hex(bytes);
|
|
4142
4100
|
}
|
|
4143
|
-
|
|
4144
|
-
this.set((this.h1 + cl + dr) | 0, (this.h2 + dl + er) | 0, (this.h3 + el + ar) | 0, (this.h4 + al + br) | 0, (this.h0 + bl + cr) | 0);
|
|
4145
|
-
}
|
|
4146
|
-
roundClean() {
|
|
4147
|
-
clean(BUF_160);
|
|
4148
|
-
}
|
|
4149
|
-
destroy() {
|
|
4150
|
-
this.destroyed = true;
|
|
4151
|
-
clean(this.buffer);
|
|
4152
|
-
this.set(0, 0, 0, 0, 0);
|
|
4101
|
+
throw new Error(`invalid hex string: ${bytes}`);
|
|
4153
4102
|
}
|
|
4154
|
-
}
|
|
4155
|
-
/**
|
|
4156
|
-
* RIPEMD-160 - a legacy hash function from 1990s.
|
|
4157
|
-
* * https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
|
|
4158
|
-
* * https://homes.esat.kuleuven.be/~bosselae/ripemd160/pdf/AB-9601/AB-9601.pdf
|
|
4159
|
-
*/
|
|
4160
|
-
const ripemd160 = /* @__PURE__ */ createHasher(() => new RIPEMD160());
|
|
4161
|
-
|
|
4162
|
-
function hash160(...input) {
|
|
4163
|
-
const buffer = Buff.join(input);
|
|
4164
|
-
const digest = ripemd160(sha256$1(buffer));
|
|
4165
|
-
return new Buff(digest);
|
|
4166
|
-
}
|
|
4167
|
-
function sha256(...input) {
|
|
4168
|
-
const buffer = Buff.join(input);
|
|
4169
|
-
const digest = sha256$1(buffer);
|
|
4170
|
-
return new Buff(digest);
|
|
4171
|
-
}
|
|
4172
|
-
function hash256(...input) {
|
|
4173
|
-
const buffer = Buff.join(input);
|
|
4174
|
-
const digest = sha256$1(sha256$1(buffer));
|
|
4175
|
-
return new Buff(digest);
|
|
4176
|
-
}
|
|
4177
|
-
function hashtag(tag) {
|
|
4178
|
-
const hash = sha256(Buff.str(tag));
|
|
4179
|
-
return Buff.join([hash, hash]);
|
|
4180
|
-
}
|
|
4181
|
-
function hash340(tag, ...input) {
|
|
4182
|
-
const htag = hashtag(tag);
|
|
4183
|
-
const data = input.map(e => new Buff(e));
|
|
4184
|
-
const pimg = Buff.join([htag, ...data]);
|
|
4185
|
-
return sha256(pimg);
|
|
4103
|
+
throw new Error(`invalid bytes: ${String(bytes)}`);
|
|
4186
4104
|
}
|
|
4187
4105
|
|
|
4188
4106
|
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
|
@@ -4190,11 +4108,9 @@ function isBytes(a) {
|
|
|
4190
4108
|
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
4191
4109
|
}
|
|
4192
4110
|
/** Asserts something is Uint8Array. */
|
|
4193
|
-
function abytes(b
|
|
4111
|
+
function abytes(b) {
|
|
4194
4112
|
if (!isBytes(b))
|
|
4195
4113
|
throw new Error('Uint8Array expected');
|
|
4196
|
-
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
4197
|
-
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
|
|
4198
4114
|
}
|
|
4199
4115
|
function isArrayOf(isString, arr) {
|
|
4200
4116
|
if (!Array.isArray(arr))
|
|
@@ -4208,7 +4124,6 @@ function isArrayOf(isString, arr) {
|
|
|
4208
4124
|
return arr.every((item) => Number.isSafeInteger(item));
|
|
4209
4125
|
}
|
|
4210
4126
|
}
|
|
4211
|
-
// no abytes: seems to have 10% slowdown. Why?!
|
|
4212
4127
|
function afn(input) {
|
|
4213
4128
|
if (typeof input !== 'function')
|
|
4214
4129
|
throw new Error('function expected');
|
|
@@ -4759,14 +4674,14 @@ const VERSION = {
|
|
|
4759
4674
|
function decode_address(address) {
|
|
4760
4675
|
const format = get_address_format(address);
|
|
4761
4676
|
if (format === null)
|
|
4762
|
-
throw new
|
|
4677
|
+
throw new ValidationError(`unrecognized address format: ${address}`, "address");
|
|
4763
4678
|
if (format === "base58")
|
|
4764
4679
|
return base58_decode(address);
|
|
4765
4680
|
if (format === "bech32")
|
|
4766
4681
|
return bech32_decode(address);
|
|
4767
4682
|
if (format === "bech32m")
|
|
4768
4683
|
return bech32m_decode(address);
|
|
4769
|
-
throw new
|
|
4684
|
+
throw new ValidationError("unable to find a matching address configuration", "address");
|
|
4770
4685
|
}
|
|
4771
4686
|
function encode_address(config) {
|
|
4772
4687
|
if (config.format === "base58")
|
|
@@ -4775,7 +4690,7 @@ function encode_address(config) {
|
|
|
4775
4690
|
return bech32_encode(config);
|
|
4776
4691
|
if (config.format === "bech32m")
|
|
4777
4692
|
return bech32m_encode(config);
|
|
4778
|
-
throw new
|
|
4693
|
+
throw new ValidationError(`unrecognized encoding format: ${config.format}`, "format");
|
|
4779
4694
|
}
|
|
4780
4695
|
function get_address_format(address) {
|
|
4781
4696
|
for (const [format, regex] of Object.entries(ENCODING_REGEX)) {
|
|
@@ -4785,8 +4700,8 @@ function get_address_format(address) {
|
|
|
4785
4700
|
return null;
|
|
4786
4701
|
}
|
|
4787
4702
|
function base58_encode(config) {
|
|
4788
|
-
Assert.ok(config.format === "base58", "encoding mismatch");
|
|
4789
|
-
Assert.exists(config.version, "must specify a version");
|
|
4703
|
+
Assert$1.ok(config.format === "base58", "encoding mismatch");
|
|
4704
|
+
Assert$1.exists(config.version, "must specify a version");
|
|
4790
4705
|
const bytes = Buff.join([config.version, config.data]);
|
|
4791
4706
|
return B58chk.encode(bytes);
|
|
4792
4707
|
}
|
|
@@ -4797,8 +4712,8 @@ function base58_decode(encoded) {
|
|
|
4797
4712
|
return { data, format: "base58", version };
|
|
4798
4713
|
}
|
|
4799
4714
|
function bech32_encode(config) {
|
|
4800
|
-
Assert.ok(config.format === "bech32", "encoding mismatch");
|
|
4801
|
-
Assert.exists(config.prefix, "prefix is required");
|
|
4715
|
+
Assert$1.ok(config.format === "bech32", "encoding mismatch");
|
|
4716
|
+
Assert$1.exists(config.prefix, "prefix is required");
|
|
4802
4717
|
const bytes = Buff.bytes(config.data);
|
|
4803
4718
|
const words = Bech32.to_words(bytes);
|
|
4804
4719
|
return Bech32.encode(config.prefix, [VERSION.bech32, ...words]);
|
|
@@ -4806,13 +4721,13 @@ function bech32_encode(config) {
|
|
|
4806
4721
|
function bech32_decode(encoded) {
|
|
4807
4722
|
const { prefix, words } = Bech32.decode(encoded);
|
|
4808
4723
|
const [version, ...rest] = words;
|
|
4809
|
-
Assert.ok(version === VERSION.bech32, "bech32 version mismatch");
|
|
4724
|
+
Assert$1.ok(version === VERSION.bech32, "bech32 version mismatch");
|
|
4810
4725
|
const data = Bech32.to_bytes(rest);
|
|
4811
4726
|
return { data, format: "bech32", prefix, version };
|
|
4812
4727
|
}
|
|
4813
4728
|
function bech32m_encode(config) {
|
|
4814
|
-
Assert.ok(config.format === "bech32m", "encoding mismatch");
|
|
4815
|
-
Assert.exists(config.prefix, "prefix is required");
|
|
4729
|
+
Assert$1.ok(config.format === "bech32m", "encoding mismatch");
|
|
4730
|
+
Assert$1.exists(config.prefix, "prefix is required");
|
|
4816
4731
|
const bytes = Buff.bytes(config.data);
|
|
4817
4732
|
const words = Bech32m.to_words(bytes);
|
|
4818
4733
|
return Bech32m.encode(config.prefix, [VERSION.bech32m, ...words]);
|
|
@@ -4820,7 +4735,7 @@ function bech32m_encode(config) {
|
|
|
4820
4735
|
function bech32m_decode(encoded) {
|
|
4821
4736
|
const { prefix, words } = Bech32m.decode(encoded);
|
|
4822
4737
|
const [version, ...rest] = words;
|
|
4823
|
-
Assert.ok(version === VERSION.bech32m, "bech32m version mismatch");
|
|
4738
|
+
Assert$1.ok(version === VERSION.bech32m, "bech32m version mismatch");
|
|
4824
4739
|
const data = Bech32m.to_bytes(rest);
|
|
4825
4740
|
return { data, format: "bech32m", prefix, version };
|
|
4826
4741
|
}
|
|
@@ -4920,7 +4835,8 @@ function get_address_info(address) {
|
|
|
4920
4835
|
const script = get_address_script(data, type);
|
|
4921
4836
|
return { data, script, type, prefix, network, size, format, version };
|
|
4922
4837
|
}
|
|
4923
|
-
throw new
|
|
4838
|
+
throw new ConfigError(`unrecognized address configuration: format=${dec.format}, size=${dec.data.length}, version=${dec.version}. ` +
|
|
4839
|
+
`Supported formats: base58 (p2pkh, p2sh), bech32 (p2wpkh, p2wsh), bech32m (p2tr)`);
|
|
4924
4840
|
}
|
|
4925
4841
|
|
|
4926
4842
|
const ADDRESS_TYPE$4 = LOCK_SCRIPT_TYPE.P2PKH;
|
|
@@ -4939,7 +4855,7 @@ function create_p2pkh_address(pubkey, network = "main") {
|
|
|
4939
4855
|
}
|
|
4940
4856
|
function create_p2pkh_script(pubkey) {
|
|
4941
4857
|
const bytes = Buff.bytes(pubkey);
|
|
4942
|
-
Assert.
|
|
4858
|
+
Assert$1.ok(bytes.length === 33, "invalid pubkey size");
|
|
4943
4859
|
const hash = hash160(bytes);
|
|
4944
4860
|
return encode_p2pkh_script(hash);
|
|
4945
4861
|
}
|
|
@@ -4949,8 +4865,8 @@ function encode_p2pkh_script(pk_hash) {
|
|
|
4949
4865
|
function encode_p2pkh_address(script, network = "main") {
|
|
4950
4866
|
const pk_hash = decode_p2pkh_script(script);
|
|
4951
4867
|
const config = get_address_config(network, ADDRESS_TYPE$4);
|
|
4952
|
-
Assert.exists(config, `unrecognized address config: ${ADDRESS_TYPE$4} on ${network}`);
|
|
4953
|
-
Assert.
|
|
4868
|
+
Assert$1.exists(config, `unrecognized address config: ${ADDRESS_TYPE$4} on ${network}`);
|
|
4869
|
+
Assert$1.ok(pk_hash.length === config.size, `invalid payload size: ${pk_hash.length} !== ${config.size}`);
|
|
4954
4870
|
return encode_address({
|
|
4955
4871
|
data: pk_hash,
|
|
4956
4872
|
format: "base58",
|
|
@@ -4959,12 +4875,12 @@ function encode_p2pkh_address(script, network = "main") {
|
|
|
4959
4875
|
}
|
|
4960
4876
|
function decode_p2pkh_address(address) {
|
|
4961
4877
|
const parsed = get_address_info(address);
|
|
4962
|
-
Assert.ok(parsed.type === "p2pkh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$4}`);
|
|
4878
|
+
Assert$1.ok(parsed.type === "p2pkh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$4}`);
|
|
4963
4879
|
return parsed;
|
|
4964
4880
|
}
|
|
4965
4881
|
function decode_p2pkh_script(script) {
|
|
4966
4882
|
const bytes = Buff.bytes(script);
|
|
4967
|
-
Assert.ok(is_p2pkh_script(script), `invalid p2pkh script`);
|
|
4883
|
+
Assert$1.ok(is_p2pkh_script(script), `invalid p2pkh script`);
|
|
4968
4884
|
return bytes.slice(3, 23);
|
|
4969
4885
|
}
|
|
4970
4886
|
|
|
@@ -4995,8 +4911,8 @@ function encode_p2sh_script(script_hash) {
|
|
|
4995
4911
|
function encode_p2sh_address(script_pk, network = "main") {
|
|
4996
4912
|
const script_hash = decode_p2sh_script(script_pk);
|
|
4997
4913
|
const config = get_address_config(network, ADDRESS_TYPE$3);
|
|
4998
|
-
Assert.exists(config, `unrecognized address config: ${ADDRESS_TYPE$3} on ${network}`);
|
|
4999
|
-
Assert.
|
|
4914
|
+
Assert$1.exists(config, `unrecognized address config: ${ADDRESS_TYPE$3} on ${network}`);
|
|
4915
|
+
Assert$1.ok(script_hash.length === config.size, `invalid payload size: ${script_hash.length} !== ${config.size}`);
|
|
5000
4916
|
return encode_address({
|
|
5001
4917
|
data: script_hash,
|
|
5002
4918
|
format: "base58",
|
|
@@ -5005,11 +4921,11 @@ function encode_p2sh_address(script_pk, network = "main") {
|
|
|
5005
4921
|
}
|
|
5006
4922
|
function decode_p2sh_address(address) {
|
|
5007
4923
|
const parsed = get_address_info(address);
|
|
5008
|
-
Assert.ok(parsed.type === "p2sh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$3}`);
|
|
4924
|
+
Assert$1.ok(parsed.type === "p2sh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$3}`);
|
|
5009
4925
|
return parsed;
|
|
5010
4926
|
}
|
|
5011
4927
|
function decode_p2sh_script(script) {
|
|
5012
|
-
Assert.ok(is_p2sh_script(script), `invalid p2sh script`);
|
|
4928
|
+
Assert$1.ok(is_p2sh_script(script), `invalid p2sh script`);
|
|
5013
4929
|
const bytes = Buff.bytes(script);
|
|
5014
4930
|
return bytes.slice(2, 22);
|
|
5015
4931
|
}
|
|
@@ -5030,7 +4946,7 @@ function create_p2tr_address(pubkey, network = "main") {
|
|
|
5030
4946
|
}
|
|
5031
4947
|
function create_p2tr_script(pubkey) {
|
|
5032
4948
|
const bytes = Buff.bytes(pubkey);
|
|
5033
|
-
Assert.
|
|
4949
|
+
Assert$1.ok(bytes.length === 32, "invalid pubkey size");
|
|
5034
4950
|
return encode_p2tr_script(bytes);
|
|
5035
4951
|
}
|
|
5036
4952
|
function encode_p2tr_script(pubkey) {
|
|
@@ -5039,8 +4955,8 @@ function encode_p2tr_script(pubkey) {
|
|
|
5039
4955
|
function encode_p2tr_address(script_pk, network = "main") {
|
|
5040
4956
|
const pubkey = decode_p2tr_script(script_pk);
|
|
5041
4957
|
const config = get_address_config(network, ADDRESS_TYPE$2);
|
|
5042
|
-
Assert.exists(config, `unrecognized address config: ${ADDRESS_TYPE$2} on ${network}`);
|
|
5043
|
-
Assert.
|
|
4958
|
+
Assert$1.exists(config, `unrecognized address config: ${ADDRESS_TYPE$2} on ${network}`);
|
|
4959
|
+
Assert$1.ok(pubkey.length === config.size, `invalid payload size: ${pubkey.length} !== ${config.size}`);
|
|
5044
4960
|
return encode_address({
|
|
5045
4961
|
data: pubkey,
|
|
5046
4962
|
format: "bech32m",
|
|
@@ -5049,11 +4965,11 @@ function encode_p2tr_address(script_pk, network = "main") {
|
|
|
5049
4965
|
}
|
|
5050
4966
|
function decode_p2tr_address(address) {
|
|
5051
4967
|
const parsed = get_address_info(address);
|
|
5052
|
-
Assert.ok(parsed.type === "p2tr", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$2}`);
|
|
4968
|
+
Assert$1.ok(parsed.type === "p2tr", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$2}`);
|
|
5053
4969
|
return parsed;
|
|
5054
4970
|
}
|
|
5055
4971
|
function decode_p2tr_script(script) {
|
|
5056
|
-
Assert.ok(is_p2tr_script(script), `invalid p2tr script`);
|
|
4972
|
+
Assert$1.ok(is_p2tr_script(script), `invalid p2tr script`);
|
|
5057
4973
|
const bytes = Buff.bytes(script);
|
|
5058
4974
|
return bytes.slice(2, 34);
|
|
5059
4975
|
}
|
|
@@ -5074,7 +4990,7 @@ function create_p2wpkh_address(pubkey, network = "main") {
|
|
|
5074
4990
|
}
|
|
5075
4991
|
function create_p2wpkh_script(pubkey) {
|
|
5076
4992
|
const bytes = Buff.bytes(pubkey);
|
|
5077
|
-
Assert.
|
|
4993
|
+
Assert$1.ok(bytes.length === 33, "invalid pubkey size");
|
|
5078
4994
|
const hash = hash160(bytes);
|
|
5079
4995
|
return encode_p2wpkh_script(hash);
|
|
5080
4996
|
}
|
|
@@ -5084,8 +5000,8 @@ function encode_p2wpkh_script(pk_hash) {
|
|
|
5084
5000
|
function encode_p2wpkh_address(script_pk, network = "main") {
|
|
5085
5001
|
const pk_hash = decode_p2wpkh_script(script_pk);
|
|
5086
5002
|
const config = get_address_config(network, ADDRESS_TYPE$1);
|
|
5087
|
-
Assert.exists(config, `unrecognized address config: ${ADDRESS_TYPE$1} on ${network}`);
|
|
5088
|
-
Assert.
|
|
5003
|
+
Assert$1.exists(config, `unrecognized address config: ${ADDRESS_TYPE$1} on ${network}`);
|
|
5004
|
+
Assert$1.ok(pk_hash.length === config.size, `invalid payload size: ${pk_hash.length} !== ${config.size}`);
|
|
5089
5005
|
return encode_address({
|
|
5090
5006
|
data: pk_hash,
|
|
5091
5007
|
format: "bech32",
|
|
@@ -5094,11 +5010,11 @@ function encode_p2wpkh_address(script_pk, network = "main") {
|
|
|
5094
5010
|
}
|
|
5095
5011
|
function decode_p2wpkh_address(address) {
|
|
5096
5012
|
const parsed = get_address_info(address);
|
|
5097
|
-
Assert.ok(parsed.type === "p2wpkh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$1}`);
|
|
5013
|
+
Assert$1.ok(parsed.type === "p2wpkh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE$1}`);
|
|
5098
5014
|
return parsed;
|
|
5099
5015
|
}
|
|
5100
5016
|
function decode_p2wpkh_script(script) {
|
|
5101
|
-
Assert.ok(is_p2wpkh_script(script), `invalid p2wpkh script`);
|
|
5017
|
+
Assert$1.ok(is_p2wpkh_script(script), `invalid p2wpkh script`);
|
|
5102
5018
|
const bytes = Buff.bytes(script);
|
|
5103
5019
|
return bytes.slice(2, 22);
|
|
5104
5020
|
}
|
|
@@ -5128,8 +5044,8 @@ function encode_p2wsh_script(script_hash) {
|
|
|
5128
5044
|
function encode_p2wsh_address(script_pk, network = "main") {
|
|
5129
5045
|
const script_hash = decode_p2wsh_script(script_pk);
|
|
5130
5046
|
const config = get_address_config(network, ADDRESS_TYPE);
|
|
5131
|
-
Assert.exists(config, `unrecognized address config: ${ADDRESS_TYPE} on ${network}`);
|
|
5132
|
-
Assert.
|
|
5047
|
+
Assert$1.exists(config, `unrecognized address config: ${ADDRESS_TYPE} on ${network}`);
|
|
5048
|
+
Assert$1.ok(script_hash.length === config.size, `invalid payload size: ${script_hash.length} !== ${config.size}`);
|
|
5133
5049
|
return encode_address({
|
|
5134
5050
|
data: script_hash,
|
|
5135
5051
|
format: "bech32",
|
|
@@ -5138,11 +5054,11 @@ function encode_p2wsh_address(script_pk, network = "main") {
|
|
|
5138
5054
|
}
|
|
5139
5055
|
function decode_p2wsh_address(address) {
|
|
5140
5056
|
const parsed = get_address_info(address);
|
|
5141
|
-
Assert.ok(parsed.type === "p2wsh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE}`);
|
|
5057
|
+
Assert$1.ok(parsed.type === "p2wsh", `address type mismatch: ${parsed.type} !== ${ADDRESS_TYPE}`);
|
|
5142
5058
|
return parsed;
|
|
5143
5059
|
}
|
|
5144
5060
|
function decode_p2wsh_script(script) {
|
|
5145
|
-
Assert.ok(is_p2wsh_script(script), `invalid p2wsh script`);
|
|
5061
|
+
Assert$1.ok(is_p2wsh_script(script), `invalid p2wsh script`);
|
|
5146
5062
|
const bytes = Buff.bytes(script);
|
|
5147
5063
|
return bytes.slice(2, 34);
|
|
5148
5064
|
}
|
|
@@ -5151,7 +5067,7 @@ function get_address(script, network = "main") {
|
|
|
5151
5067
|
const bytes = Buff.bytes(script);
|
|
5152
5068
|
const type = get_lock_script_type(bytes);
|
|
5153
5069
|
if (type === null)
|
|
5154
|
-
throw new
|
|
5070
|
+
throw new ConfigError("Unknown or unsupported locking script type");
|
|
5155
5071
|
switch (type) {
|
|
5156
5072
|
case LOCK_SCRIPT_TYPE.P2PKH:
|
|
5157
5073
|
return P2PKH.encode_address(script, network);
|
|
@@ -5164,7 +5080,7 @@ function get_address(script, network = "main") {
|
|
|
5164
5080
|
case LOCK_SCRIPT_TYPE.P2TR:
|
|
5165
5081
|
return P2TR.encode_address(script, network);
|
|
5166
5082
|
default:
|
|
5167
|
-
throw new
|
|
5083
|
+
throw new ConfigError(`unknown script type: ${type}`);
|
|
5168
5084
|
}
|
|
5169
5085
|
}
|
|
5170
5086
|
function parse_address(address) {
|
|
@@ -5191,14 +5107,14 @@ var LocktimeField;
|
|
|
5191
5107
|
function encode_locktime(locktime) {
|
|
5192
5108
|
switch (locktime.type) {
|
|
5193
5109
|
case "timelock":
|
|
5194
|
-
Assert.ok(locktime.stamp >= LOCKTIME_THRESHOLD, "Invalid timestamp");
|
|
5110
|
+
Assert$1.ok(locktime.stamp >= LOCKTIME_THRESHOLD, "Invalid timestamp");
|
|
5195
5111
|
return locktime.stamp;
|
|
5196
5112
|
case "heightlock":
|
|
5197
|
-
Assert.ok(locktime.height > 0, "height must be greater than 0");
|
|
5198
|
-
Assert.ok(locktime.height < LOCKTIME_THRESHOLD, "invalid block height");
|
|
5113
|
+
Assert$1.ok(locktime.height > 0, "height must be greater than 0");
|
|
5114
|
+
Assert$1.ok(locktime.height < LOCKTIME_THRESHOLD, "invalid block height");
|
|
5199
5115
|
return locktime.height;
|
|
5200
5116
|
default:
|
|
5201
|
-
throw new
|
|
5117
|
+
throw new ConfigError(`Invalid locktime type: expected 'timelock' or 'heightlock'`);
|
|
5202
5118
|
}
|
|
5203
5119
|
}
|
|
5204
5120
|
function decode_locktime(locktime) {
|
|
@@ -5253,7 +5169,7 @@ function verify_inscription_id(inscription_id) {
|
|
|
5253
5169
|
}
|
|
5254
5170
|
function assert_inscription_id(inscription_id) {
|
|
5255
5171
|
if (!verify_inscription_id(inscription_id)) {
|
|
5256
|
-
throw new
|
|
5172
|
+
throw new ValidationError(`invalid inscription id: "${inscription_id}". Expected format: <64-char-txid>i<index> (e.g., "abc123...i0")`);
|
|
5257
5173
|
}
|
|
5258
5174
|
}
|
|
5259
5175
|
function encode_rune_id(block_height, block_index) {
|
|
@@ -5272,7 +5188,7 @@ function verify_rune_id(rune_id) {
|
|
|
5272
5188
|
}
|
|
5273
5189
|
function assert_rune_id(rune_id) {
|
|
5274
5190
|
if (!verify_rune_id(rune_id)) {
|
|
5275
|
-
throw new
|
|
5191
|
+
throw new ValidationError(`invalid rune id: "${rune_id}". Expected format: <block_height>:<block_index> (e.g., "840000:1")`);
|
|
5276
5192
|
}
|
|
5277
5193
|
}
|
|
5278
5194
|
function encode_outpoint(txid, vout) {
|
|
@@ -5288,7 +5204,7 @@ function verify_outpoint(outpoint) {
|
|
|
5288
5204
|
}
|
|
5289
5205
|
function assert_outpoint(outpoint) {
|
|
5290
5206
|
if (!verify_outpoint(outpoint)) {
|
|
5291
|
-
throw new
|
|
5207
|
+
throw new ValidationError(`invalid outpoint: "${outpoint}". Expected format: <64-char-txid>:<vout> (e.g., "abc123...:0")`);
|
|
5292
5208
|
}
|
|
5293
5209
|
}
|
|
5294
5210
|
|
|
@@ -5412,14 +5328,14 @@ function get_op_code(num) {
|
|
|
5412
5328
|
if (v === num)
|
|
5413
5329
|
return k;
|
|
5414
5330
|
}
|
|
5415
|
-
throw new
|
|
5331
|
+
throw new ValidationError(`opcode not found for value: ${num} (0x${num.toString(16)}). Valid range is 0x00-0xba`);
|
|
5416
5332
|
}
|
|
5417
5333
|
function get_asm_code(string) {
|
|
5418
5334
|
for (const [k, v] of Object.entries(OPCODE_MAP)) {
|
|
5419
5335
|
if (k === string)
|
|
5420
5336
|
return Number(v);
|
|
5421
5337
|
}
|
|
5422
|
-
throw new
|
|
5338
|
+
throw new ValidationError(`opcode not found: "${string}". Valid opcodes start with "OP_" (e.g., OP_DUP, OP_CHECKSIG)`);
|
|
5423
5339
|
}
|
|
5424
5340
|
function get_op_type(word) {
|
|
5425
5341
|
switch (true) {
|
|
@@ -5436,7 +5352,7 @@ function get_op_type(word) {
|
|
|
5436
5352
|
case word <= 254:
|
|
5437
5353
|
return "opcode";
|
|
5438
5354
|
default:
|
|
5439
|
-
throw new
|
|
5355
|
+
throw new ValidationError(`invalid word value: ${word}. Expected 0-254`);
|
|
5440
5356
|
}
|
|
5441
5357
|
}
|
|
5442
5358
|
function is_valid_op(word) {
|
|
@@ -5482,7 +5398,7 @@ function decode_script(script) {
|
|
|
5482
5398
|
stack.push(stream.read(word).hex);
|
|
5483
5399
|
}
|
|
5484
5400
|
catch {
|
|
5485
|
-
throw new
|
|
5401
|
+
throw new DecodingError(`Malformed script: varint push at position ${count - 1} requires ${word} bytes but stream exhausted`, count - 1);
|
|
5486
5402
|
}
|
|
5487
5403
|
count += word;
|
|
5488
5404
|
break;
|
|
@@ -5491,13 +5407,13 @@ function decode_script(script) {
|
|
|
5491
5407
|
word_size = stream.read(1).reverse().num;
|
|
5492
5408
|
}
|
|
5493
5409
|
catch {
|
|
5494
|
-
throw new
|
|
5410
|
+
throw new DecodingError(`Malformed script: PUSHDATA1 at position ${count - 1} missing size byte`, count - 1);
|
|
5495
5411
|
}
|
|
5496
5412
|
try {
|
|
5497
5413
|
stack.push(stream.read(word_size).hex);
|
|
5498
5414
|
}
|
|
5499
5415
|
catch {
|
|
5500
|
-
throw new
|
|
5416
|
+
throw new DecodingError(`Malformed script: PUSHDATA1 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
5501
5417
|
}
|
|
5502
5418
|
count += word_size + 1;
|
|
5503
5419
|
break;
|
|
@@ -5506,13 +5422,13 @@ function decode_script(script) {
|
|
|
5506
5422
|
word_size = stream.read(2).reverse().num;
|
|
5507
5423
|
}
|
|
5508
5424
|
catch {
|
|
5509
|
-
throw new
|
|
5425
|
+
throw new DecodingError(`Malformed script: PUSHDATA2 at position ${count - 1} missing size bytes`, count - 1);
|
|
5510
5426
|
}
|
|
5511
5427
|
try {
|
|
5512
5428
|
stack.push(stream.read(word_size).hex);
|
|
5513
5429
|
}
|
|
5514
5430
|
catch {
|
|
5515
|
-
throw new
|
|
5431
|
+
throw new DecodingError(`Malformed script: PUSHDATA2 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
5516
5432
|
}
|
|
5517
5433
|
count += word_size + 2;
|
|
5518
5434
|
break;
|
|
@@ -5521,24 +5437,24 @@ function decode_script(script) {
|
|
|
5521
5437
|
word_size = stream.read(4).reverse().num;
|
|
5522
5438
|
}
|
|
5523
5439
|
catch {
|
|
5524
|
-
throw new
|
|
5440
|
+
throw new DecodingError(`Malformed script: PUSHDATA4 at position ${count - 1} missing size bytes`, count - 1);
|
|
5525
5441
|
}
|
|
5526
5442
|
try {
|
|
5527
5443
|
stack.push(stream.read(word_size).hex);
|
|
5528
5444
|
}
|
|
5529
5445
|
catch {
|
|
5530
|
-
throw new
|
|
5446
|
+
throw new DecodingError(`Malformed script: PUSHDATA4 at position ${count - 1} requires ${word_size} bytes but stream exhausted`, count - 1);
|
|
5531
5447
|
}
|
|
5532
5448
|
count += word_size + 4;
|
|
5533
5449
|
break;
|
|
5534
5450
|
case "opcode":
|
|
5535
5451
|
if (!is_valid_op(word)) {
|
|
5536
|
-
throw new
|
|
5452
|
+
throw new DecodingError(`Invalid OPCODE: ${word}`, count - 1);
|
|
5537
5453
|
}
|
|
5538
5454
|
stack.push(get_op_code(word));
|
|
5539
5455
|
break;
|
|
5540
5456
|
default:
|
|
5541
|
-
throw new
|
|
5457
|
+
throw new DecodingError(`Word type undefined: ${word}`, count - 1);
|
|
5542
5458
|
}
|
|
5543
5459
|
}
|
|
5544
5460
|
return stack;
|
|
@@ -5563,7 +5479,7 @@ function encode_script(words, varint = false) {
|
|
|
5563
5479
|
}
|
|
5564
5480
|
const buffer = Buff.join(bytes);
|
|
5565
5481
|
if (buffer.length > MAX_SCRIPT_SIZE) {
|
|
5566
|
-
throw new
|
|
5482
|
+
throw new ValidationError(`script size ${buffer.length} exceeds consensus limit of ${MAX_SCRIPT_SIZE} bytes`);
|
|
5567
5483
|
}
|
|
5568
5484
|
return varint
|
|
5569
5485
|
? buffer.prepend(Buff.create_varint(buffer.length, "le"))
|
|
@@ -5590,7 +5506,7 @@ function encode_script_word(word) {
|
|
|
5590
5506
|
buff = new Buff(word);
|
|
5591
5507
|
}
|
|
5592
5508
|
else {
|
|
5593
|
-
throw new
|
|
5509
|
+
throw new ValidationError(`invalid script word type: ${typeof word}. Expected string, number, or Uint8Array`);
|
|
5594
5510
|
}
|
|
5595
5511
|
if (buff.length === 1 && buff[0] <= 16) {
|
|
5596
5512
|
if (buff[0] !== 0)
|
|
@@ -5631,7 +5547,7 @@ function get_size_varint(size) {
|
|
|
5631
5547
|
case size >= 0x100 && size <= MAX_WORD_SIZE:
|
|
5632
5548
|
return Buff.join([OP_PUSHDATA2, Buff.num(size, 2, "le")]);
|
|
5633
5549
|
default:
|
|
5634
|
-
throw new
|
|
5550
|
+
throw new ValidationError(`invalid script word size: ${size}. Maximum allowed is ${MAX_WORD_SIZE} bytes`);
|
|
5635
5551
|
}
|
|
5636
5552
|
}
|
|
5637
5553
|
|
|
@@ -5689,16 +5605,17 @@ function create_envelope(data) {
|
|
|
5689
5605
|
function parse_envelopes(script) {
|
|
5690
5606
|
const words = decode_script(script);
|
|
5691
5607
|
const start_idx = words.indexOf("OP_0");
|
|
5692
|
-
Assert.ok(start_idx !== -1, "inscription envelope not found");
|
|
5608
|
+
Assert$1.ok(start_idx !== -1, "inscription envelope not found");
|
|
5693
5609
|
const envelopes = [];
|
|
5694
5610
|
for (let idx = start_idx; idx < words.length; idx++) {
|
|
5695
|
-
Assert.ok(
|
|
5696
|
-
Assert.ok(words[idx +
|
|
5697
|
-
|
|
5698
|
-
|
|
5611
|
+
Assert$1.ok(idx + 2 < words.length, "incomplete envelope: missing OP_IF or magic bytes");
|
|
5612
|
+
Assert$1.ok(words[idx + 1] === "OP_IF", "OP_IF missing from envelope");
|
|
5613
|
+
Assert$1.ok(words[idx + 2] === "6f7264", "magic bytes missing from envelope");
|
|
5614
|
+
const stop_idx = words.indexOf("OP_ENDIF", idx);
|
|
5615
|
+
Assert$1.ok(stop_idx !== -1, "inscription envelope missing OP_ENDIF statement");
|
|
5699
5616
|
const env = words.slice(idx + 3, stop_idx);
|
|
5700
5617
|
envelopes.push(env);
|
|
5701
|
-
idx
|
|
5618
|
+
idx = stop_idx;
|
|
5702
5619
|
}
|
|
5703
5620
|
return envelopes;
|
|
5704
5621
|
}
|
|
@@ -5744,8 +5661,21 @@ function parse_record(envelope) {
|
|
|
5744
5661
|
function decode_bytes(bytes) {
|
|
5745
5662
|
return Buff.bytes(bytes).hex;
|
|
5746
5663
|
}
|
|
5664
|
+
function normalize_script_word(word) {
|
|
5665
|
+
if (typeof word === "string" && word.startsWith("OP_")) {
|
|
5666
|
+
if (word === "OP_0")
|
|
5667
|
+
return Buff.num(0).hex;
|
|
5668
|
+
const match = word.match(/^OP_(\d+)$/);
|
|
5669
|
+
if (match) {
|
|
5670
|
+
const num = parseInt(match[1], 10);
|
|
5671
|
+
if (num >= 1 && num <= 16)
|
|
5672
|
+
return Buff.num(num).hex;
|
|
5673
|
+
}
|
|
5674
|
+
}
|
|
5675
|
+
return word;
|
|
5676
|
+
}
|
|
5747
5677
|
function encode_id(identifier) {
|
|
5748
|
-
Assert.ok(identifier.includes("i"), "identifier must include an index");
|
|
5678
|
+
Assert$1.ok(identifier.includes("i"), "identifier must include an index");
|
|
5749
5679
|
const parts = identifier.split("i");
|
|
5750
5680
|
const bytes = Buff.hex(parts[0]);
|
|
5751
5681
|
const idx = Number(parts[1]);
|
|
@@ -5754,6 +5684,9 @@ function encode_id(identifier) {
|
|
|
5754
5684
|
}
|
|
5755
5685
|
function decode_id(identifier) {
|
|
5756
5686
|
const bytes = Buff.bytes(identifier);
|
|
5687
|
+
if (bytes.length === 32) {
|
|
5688
|
+
return `${bytes.reverse().hex}i0`;
|
|
5689
|
+
}
|
|
5757
5690
|
const idx = bytes.at(-1) ?? 0;
|
|
5758
5691
|
const txid = bytes.slice(0, -1).reverse().hex;
|
|
5759
5692
|
return `${txid}i${String(idx)}`;
|
|
@@ -5762,7 +5695,7 @@ function encode_pointer(pointer) {
|
|
|
5762
5695
|
return Buff.num(pointer).reverse().hex;
|
|
5763
5696
|
}
|
|
5764
5697
|
function decode_pointer(bytes) {
|
|
5765
|
-
return Buff.bytes(bytes).reverse().num;
|
|
5698
|
+
return Buff.bytes(normalize_script_word(bytes)).reverse().num;
|
|
5766
5699
|
}
|
|
5767
5700
|
function encode_label(label) {
|
|
5768
5701
|
return Buff.str(label).hex;
|
|
@@ -5797,12 +5730,16 @@ function encode_rune_label(label) {
|
|
|
5797
5730
|
if (char >= "A" && char <= "Z") {
|
|
5798
5731
|
big = big * _26n + BigInt(char.charCodeAt(0) - ("A".charCodeAt(0) - 1));
|
|
5799
5732
|
}
|
|
5733
|
+
else {
|
|
5734
|
+
throw new ValidationError(`invalid character in rune label: '${char}' (only A-Z allowed)`, "label");
|
|
5735
|
+
}
|
|
5800
5736
|
}
|
|
5801
5737
|
big = big - _1n;
|
|
5802
5738
|
return Buff.big(big).reverse().hex;
|
|
5803
5739
|
}
|
|
5804
5740
|
function decode_rune_label(label) {
|
|
5805
|
-
|
|
5741
|
+
const normalized = normalize_script_word(label);
|
|
5742
|
+
let big = Buff.bytes(normalized).reverse().big;
|
|
5806
5743
|
big = big + _1n;
|
|
5807
5744
|
let result = "";
|
|
5808
5745
|
while (big > _0n) {
|
|
@@ -5839,7 +5776,7 @@ function encode_sequence(data) {
|
|
|
5839
5776
|
const stamp = parse_stamp(data.stamp);
|
|
5840
5777
|
return (TIMELOCK_TYPE | (stamp & TIMELOCK_VALUE_MASK)) >>> 0;
|
|
5841
5778
|
}
|
|
5842
|
-
throw new
|
|
5779
|
+
throw new ValidationError(`invalid timelock mode: "${data.mode}". Valid modes are "height" or "stamp"`);
|
|
5843
5780
|
}
|
|
5844
5781
|
function decode_sequence(sequence) {
|
|
5845
5782
|
const seq = parse_sequence(sequence);
|
|
@@ -5849,13 +5786,13 @@ function decode_sequence(sequence) {
|
|
|
5849
5786
|
if (seq & TIMELOCK_TYPE) {
|
|
5850
5787
|
const stamp = value * TIMELOCK_GRANULARITY;
|
|
5851
5788
|
if (stamp > 0xffffffff) {
|
|
5852
|
-
throw new
|
|
5789
|
+
throw new ValidationError(`decoded timestamp ${stamp} exceeds 32-bit limit (max: ${0xffffffff})`);
|
|
5853
5790
|
}
|
|
5854
5791
|
return { mode: "stamp", stamp };
|
|
5855
5792
|
}
|
|
5856
5793
|
else {
|
|
5857
5794
|
if (value > TIMELOCK_VALUE_MAX) {
|
|
5858
|
-
throw new
|
|
5795
|
+
throw new ValidationError(`decoded height ${value} exceeds maximum (${TIMELOCK_VALUE_MAX})`);
|
|
5859
5796
|
}
|
|
5860
5797
|
return { mode: "height", height: value };
|
|
5861
5798
|
}
|
|
@@ -5863,17 +5800,17 @@ function decode_sequence(sequence) {
|
|
|
5863
5800
|
function parse_sequence(sequence) {
|
|
5864
5801
|
const seq = typeof sequence === "string" ? parseInt(sequence, 16) : sequence;
|
|
5865
5802
|
if (!Number.isInteger(seq) || seq < 0 || seq > 0xffffffff) {
|
|
5866
|
-
throw new
|
|
5803
|
+
throw new ValidationError(`invalid sequence value: ${seq}. Must be an integer between 0 and 0xffffffff`);
|
|
5867
5804
|
}
|
|
5868
5805
|
return seq;
|
|
5869
5806
|
}
|
|
5870
5807
|
function parse_stamp(stamp) {
|
|
5871
5808
|
if (stamp === undefined || !Number.isInteger(stamp)) {
|
|
5872
|
-
throw new
|
|
5809
|
+
throw new ValidationError(`timestamp must be an integer, got: ${stamp}`);
|
|
5873
5810
|
}
|
|
5874
5811
|
const ts = Math.floor(stamp / TIMELOCK_GRANULARITY);
|
|
5875
5812
|
if (!Number.isInteger(ts) || ts < 0 || ts > TIMELOCK_VALUE_MAX) {
|
|
5876
|
-
throw new
|
|
5813
|
+
throw new ValidationError(`timelock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX} (in 512-second increments)`);
|
|
5877
5814
|
}
|
|
5878
5815
|
return ts;
|
|
5879
5816
|
}
|
|
@@ -5882,7 +5819,7 @@ function parse_height(height) {
|
|
|
5882
5819
|
!Number.isInteger(height) ||
|
|
5883
5820
|
height < 0 ||
|
|
5884
5821
|
height > TIMELOCK_VALUE_MAX) {
|
|
5885
|
-
throw new
|
|
5822
|
+
throw new ValidationError(`heightlock value must be an integer between 0 and ${TIMELOCK_VALUE_MAX}, got: ${height}`);
|
|
5886
5823
|
}
|
|
5887
5824
|
return height;
|
|
5888
5825
|
}
|
|
@@ -5943,10 +5880,10 @@ var index$6 = /*#__PURE__*/Object.freeze({
|
|
|
5943
5880
|
const MAX_TX_SIZE = 4_000_000;
|
|
5944
5881
|
const MAX_TX_ELEMENTS = 100_000;
|
|
5945
5882
|
function decode_tx(txdata, use_segwit = true) {
|
|
5946
|
-
Assert.
|
|
5883
|
+
Assert$1.ok(typeof txdata === "string" || txdata instanceof Uint8Array, "txdata must be hex or bytes");
|
|
5947
5884
|
const txSize = typeof txdata === "string" ? txdata.length / 2 : txdata.length;
|
|
5948
5885
|
if (txSize > MAX_TX_SIZE) {
|
|
5949
|
-
throw new
|
|
5886
|
+
throw new DecodingError(`Transaction size ${txSize} exceeds maximum ${MAX_TX_SIZE} bytes`);
|
|
5950
5887
|
}
|
|
5951
5888
|
const stream = new Stream(txdata);
|
|
5952
5889
|
const version = read_version(stream);
|
|
@@ -5973,7 +5910,7 @@ function check_witness_flag(stream) {
|
|
|
5973
5910
|
return true;
|
|
5974
5911
|
}
|
|
5975
5912
|
else {
|
|
5976
|
-
throw new
|
|
5913
|
+
throw new DecodingError(`Invalid witness flag: ${flag}`, 1);
|
|
5977
5914
|
}
|
|
5978
5915
|
}
|
|
5979
5916
|
return false;
|
|
@@ -5982,7 +5919,7 @@ function read_inputs(stream) {
|
|
|
5982
5919
|
const inputs = [];
|
|
5983
5920
|
const vinCount = stream.read_varint();
|
|
5984
5921
|
if (vinCount > MAX_TX_ELEMENTS) {
|
|
5985
|
-
throw new
|
|
5922
|
+
throw new DecodingError(`Input count ${vinCount} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
5986
5923
|
}
|
|
5987
5924
|
for (let i = 0; i < vinCount; i++) {
|
|
5988
5925
|
const txinput = read_vin(stream);
|
|
@@ -6023,14 +5960,15 @@ function read_outputs(stream) {
|
|
|
6023
5960
|
const outputs = [];
|
|
6024
5961
|
const vcount = stream.read_varint();
|
|
6025
5962
|
if (vcount > MAX_TX_ELEMENTS) {
|
|
6026
|
-
throw new
|
|
5963
|
+
throw new DecodingError(`Output count ${vcount} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
6027
5964
|
}
|
|
6028
5965
|
for (let i = 0; i < vcount; i++) {
|
|
6029
5966
|
try {
|
|
6030
5967
|
outputs.push(read_vout(stream));
|
|
6031
5968
|
}
|
|
6032
|
-
catch (
|
|
6033
|
-
|
|
5969
|
+
catch (error) {
|
|
5970
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5971
|
+
throw new DecodingError(`Failed to decode output at index ${i}: ${message}`);
|
|
6034
5972
|
}
|
|
6035
5973
|
}
|
|
6036
5974
|
return outputs;
|
|
@@ -6038,19 +5976,19 @@ function read_outputs(stream) {
|
|
|
6038
5976
|
function read_vout(stream) {
|
|
6039
5977
|
const value = stream.read(8).reverse().big;
|
|
6040
5978
|
const script_pk = read_payload(stream);
|
|
6041
|
-
Assert.exists(script_pk, "failed to decode script_pk");
|
|
5979
|
+
Assert$1.exists(script_pk, "failed to decode script_pk");
|
|
6042
5980
|
return { value, script_pk };
|
|
6043
5981
|
}
|
|
6044
5982
|
function read_witness(stream) {
|
|
6045
5983
|
const stack = [];
|
|
6046
5984
|
const count = stream.read_varint();
|
|
6047
5985
|
if (count > MAX_TX_ELEMENTS) {
|
|
6048
|
-
throw new
|
|
5986
|
+
throw new DecodingError(`Witness element count ${count} exceeds maximum ${MAX_TX_ELEMENTS}`);
|
|
6049
5987
|
}
|
|
6050
5988
|
for (let i = 0; i < count; i++) {
|
|
6051
5989
|
const element = read_payload(stream);
|
|
6052
5990
|
if (element === null) {
|
|
6053
|
-
throw new
|
|
5991
|
+
throw new DecodingError(`Failed to decode witness element at index ${i}`);
|
|
6054
5992
|
}
|
|
6055
5993
|
stack.push(element);
|
|
6056
5994
|
}
|
|
@@ -6059,7 +5997,7 @@ function read_witness(stream) {
|
|
|
6059
5997
|
function read_payload(stream) {
|
|
6060
5998
|
const size = stream.read_varint("le");
|
|
6061
5999
|
if (size > MAX_VARINT_SIZE) {
|
|
6062
|
-
throw new
|
|
6000
|
+
throw new DecodingError(`Payload size ${size} exceeds maximum ${MAX_VARINT_SIZE}`);
|
|
6063
6001
|
}
|
|
6064
6002
|
return size > 0 ? stream.read(size).hex : null;
|
|
6065
6003
|
}
|
|
@@ -7242,7 +7180,7 @@ class Doc {
|
|
|
7242
7180
|
const version = {
|
|
7243
7181
|
major: 4,
|
|
7244
7182
|
minor: 3,
|
|
7245
|
-
patch:
|
|
7183
|
+
patch: 6,
|
|
7246
7184
|
};
|
|
7247
7185
|
|
|
7248
7186
|
const $ZodType = /*@__PURE__*/ $constructor("$ZodType", (inst, def) => {
|
|
@@ -8300,11 +8238,9 @@ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
|
|
|
8300
8238
|
if (keyResult instanceof Promise) {
|
|
8301
8239
|
throw new Error("Async schemas not supported in object keys currently");
|
|
8302
8240
|
}
|
|
8303
|
-
// Numeric string fallback: if key
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
keyResult.issues.length &&
|
|
8307
|
-
keyResult.issues.some((iss) => iss.code === "invalid_type" && iss.expected === "number");
|
|
8241
|
+
// Numeric string fallback: if key is a numeric string and failed, retry with Number(key)
|
|
8242
|
+
// This handles z.number(), z.literal([1, 2, 3]), and unions containing numeric literals
|
|
8243
|
+
const checkNumericKey = typeof key === "string" && number$1.test(key) && keyResult.issues.length;
|
|
8308
8244
|
if (checkNumericKey) {
|
|
8309
8245
|
const retryResult = def.keyType._zod.run({ value: Number(key), issues: [] }, ctx);
|
|
8310
8246
|
if (retryResult instanceof Promise) {
|
|
@@ -9505,7 +9441,7 @@ function finalize(ctx, schema) {
|
|
|
9505
9441
|
}
|
|
9506
9442
|
}
|
|
9507
9443
|
// When ref was extracted to $defs, remove properties that match the definition
|
|
9508
|
-
if (refSchema.$ref) {
|
|
9444
|
+
if (refSchema.$ref && refSeen.def) {
|
|
9509
9445
|
for (const key in schema) {
|
|
9510
9446
|
if (key === "$ref" || key === "allOf")
|
|
9511
9447
|
continue;
|
|
@@ -10907,32 +10843,59 @@ var index$5 = /*#__PURE__*/Object.freeze({
|
|
|
10907
10843
|
tx: tx
|
|
10908
10844
|
});
|
|
10909
10845
|
|
|
10846
|
+
function format_zod_error(error) {
|
|
10847
|
+
const issues = error.issues.map((issue) => {
|
|
10848
|
+
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
10849
|
+
return `${path}${issue.message}`;
|
|
10850
|
+
});
|
|
10851
|
+
return issues.join("; ");
|
|
10852
|
+
}
|
|
10910
10853
|
function assert_tx_template(txdata) {
|
|
10911
|
-
tx_template.
|
|
10854
|
+
const result = tx_template.safeParse(txdata);
|
|
10855
|
+
if (!result.success) {
|
|
10856
|
+
throw new ValidationError(`invalid transaction template: ${format_zod_error(result.error)}`);
|
|
10857
|
+
}
|
|
10912
10858
|
}
|
|
10913
10859
|
function assert_has_prevouts(vin) {
|
|
10914
|
-
|
|
10915
|
-
|
|
10860
|
+
const missingIdx = vin.findIndex((txin) => txin.prevout === null || txin.prevout === undefined);
|
|
10861
|
+
if (missingIdx !== -1) {
|
|
10862
|
+
throw new ValidationError(`transaction input at index ${missingIdx} is missing prevout data. ` +
|
|
10863
|
+
`Prevout (previous output) is required for signing`);
|
|
10916
10864
|
}
|
|
10917
10865
|
}
|
|
10918
10866
|
function assert_tx_data(txdata) {
|
|
10919
|
-
tx_data.
|
|
10867
|
+
const result = tx_data.safeParse(txdata);
|
|
10868
|
+
if (!result.success) {
|
|
10869
|
+
throw new ValidationError(`invalid transaction data: ${format_zod_error(result.error)}`);
|
|
10870
|
+
}
|
|
10920
10871
|
}
|
|
10921
10872
|
function assert_tx_spend_data(txdata) {
|
|
10922
10873
|
assert_tx_data(txdata);
|
|
10923
10874
|
assert_has_prevouts(txdata.vin);
|
|
10924
10875
|
}
|
|
10925
10876
|
function assert_tx_input(tx_input$1) {
|
|
10926
|
-
tx_input.
|
|
10877
|
+
const result = tx_input.safeParse(tx_input$1);
|
|
10878
|
+
if (!result.success) {
|
|
10879
|
+
throw new ValidationError(`invalid transaction input: ${format_zod_error(result.error)}`);
|
|
10880
|
+
}
|
|
10927
10881
|
}
|
|
10928
10882
|
function assert_tx_output(tx_output$1) {
|
|
10929
|
-
tx_output.
|
|
10883
|
+
const result = tx_output.safeParse(tx_output$1);
|
|
10884
|
+
if (!result.success) {
|
|
10885
|
+
throw new ValidationError(`invalid transaction output: ${format_zod_error(result.error)}`);
|
|
10886
|
+
}
|
|
10930
10887
|
}
|
|
10931
10888
|
function assert_vin_template(vin) {
|
|
10932
|
-
vin_template.
|
|
10889
|
+
const result = vin_template.safeParse(vin);
|
|
10890
|
+
if (!result.success) {
|
|
10891
|
+
throw new ValidationError(`invalid input template: ${format_zod_error(result.error)}`);
|
|
10892
|
+
}
|
|
10933
10893
|
}
|
|
10934
10894
|
function assert_vout_template(vout) {
|
|
10935
|
-
vout_template.
|
|
10895
|
+
const result = vout_template.safeParse(vout);
|
|
10896
|
+
if (!result.success) {
|
|
10897
|
+
throw new ValidationError(`invalid output template: ${format_zod_error(result.error)}`);
|
|
10898
|
+
}
|
|
10936
10899
|
}
|
|
10937
10900
|
|
|
10938
10901
|
function encode_tx(txdata, use_segwit = true) {
|
|
@@ -11015,7 +10978,7 @@ function encode_tx_locktime(locktime) {
|
|
|
11015
10978
|
}
|
|
11016
10979
|
function encode_script_data(script) {
|
|
11017
10980
|
if (script !== null) {
|
|
11018
|
-
Assert.is_hex(script);
|
|
10981
|
+
Assert$1.is_hex(script);
|
|
11019
10982
|
return Buff.hex(script).prefix_varint("le");
|
|
11020
10983
|
}
|
|
11021
10984
|
else {
|
|
@@ -11033,10 +10996,10 @@ function parse_tx(txdata, prevouts) {
|
|
|
11033
10996
|
tx = create_tx(txdata);
|
|
11034
10997
|
}
|
|
11035
10998
|
if (prevouts) {
|
|
11036
|
-
Assert.has_items(prevouts, "prevouts must be a non-empty array");
|
|
10999
|
+
Assert$1.has_items(prevouts, "prevouts must be a non-empty array");
|
|
11037
11000
|
for (const [idx, vin] of tx.vin.entries()) {
|
|
11038
11001
|
const prevout = prevouts.at(idx);
|
|
11039
|
-
Assert.exists(prevout, `prevout not found for input index: ${idx}`);
|
|
11002
|
+
Assert$1.exists(prevout, `prevout not found for input index: ${idx}`);
|
|
11040
11003
|
vin.prevout = create_tx_output(prevout);
|
|
11041
11004
|
}
|
|
11042
11005
|
}
|
|
@@ -11052,14 +11015,14 @@ function serialize_tx(txdata) {
|
|
|
11052
11015
|
if (e.prevout !== null) {
|
|
11053
11016
|
vin.push({
|
|
11054
11017
|
script_pk: e.prevout.script_pk,
|
|
11055
|
-
value:
|
|
11018
|
+
value: String(e.prevout.value),
|
|
11056
11019
|
});
|
|
11057
11020
|
}
|
|
11058
11021
|
}
|
|
11059
11022
|
for (const e of tx.vout) {
|
|
11060
11023
|
vout.push({
|
|
11061
11024
|
script_pk: e.script_pk,
|
|
11062
|
-
value:
|
|
11025
|
+
value: String(e.value),
|
|
11063
11026
|
});
|
|
11064
11027
|
}
|
|
11065
11028
|
return { version, locktime, vin, vout };
|
|
@@ -11079,7 +11042,7 @@ function get_txid(txdata) {
|
|
|
11079
11042
|
buffer = encode_tx(txdata, false);
|
|
11080
11043
|
}
|
|
11081
11044
|
else if (typeof txdata === "string") {
|
|
11082
|
-
Assert.is_hex(txdata);
|
|
11045
|
+
Assert$1.is_hex(txdata);
|
|
11083
11046
|
buffer = transcode_tx(txdata, false);
|
|
11084
11047
|
}
|
|
11085
11048
|
else {
|
|
@@ -11105,7 +11068,7 @@ function get_tx_value(txdata) {
|
|
|
11105
11068
|
function get_prevouts(txdata) {
|
|
11106
11069
|
assert_tx_template(txdata);
|
|
11107
11070
|
const prevouts = txdata.vin.map((e) => e.prevout);
|
|
11108
|
-
Assert.ok(prevouts.every((e) => e !== null), "prevouts missing from tx");
|
|
11071
|
+
Assert$1.ok(prevouts.every((e) => e !== null), "prevouts missing from tx");
|
|
11109
11072
|
return prevouts;
|
|
11110
11073
|
}
|
|
11111
11074
|
function normalize_sequence(sequence) {
|
|
@@ -11133,7 +11096,7 @@ function normalize_prevout(prevout) {
|
|
|
11133
11096
|
|
|
11134
11097
|
function create_coinbase_input(config) {
|
|
11135
11098
|
assert_vin_template(config);
|
|
11136
|
-
Assert.exists(config.coinbase, "coinbase is required");
|
|
11099
|
+
Assert$1.exists(config.coinbase, "coinbase is required");
|
|
11137
11100
|
const txid = COINBASE.TXID;
|
|
11138
11101
|
const vout = COINBASE.VOUT;
|
|
11139
11102
|
const coinbase = config.coinbase;
|
|
@@ -11151,8 +11114,8 @@ function create_coinbase_input(config) {
|
|
|
11151
11114
|
}
|
|
11152
11115
|
function create_virtual_input(config) {
|
|
11153
11116
|
assert_vin_template(config);
|
|
11154
|
-
Assert.is_empty(config.coinbase, "coinbase is not allowed");
|
|
11155
|
-
Assert.is_empty(config.prevout, "prevout is not allowed");
|
|
11117
|
+
Assert$1.is_empty(config.coinbase, "coinbase is not allowed");
|
|
11118
|
+
Assert$1.is_empty(config.prevout, "prevout is not allowed");
|
|
11156
11119
|
const { txid, vout, script_sig = null, witness = [] } = config;
|
|
11157
11120
|
const sequence = normalize_sequence(config.sequence);
|
|
11158
11121
|
return {
|
|
@@ -11167,7 +11130,7 @@ function create_virtual_input(config) {
|
|
|
11167
11130
|
}
|
|
11168
11131
|
function create_spend_input(config) {
|
|
11169
11132
|
assert_vin_template(config);
|
|
11170
|
-
Assert.exists(config.prevout, "prevout is required");
|
|
11133
|
+
Assert$1.exists(config.prevout, "prevout is required");
|
|
11171
11134
|
const { txid, vout, script_sig = null, witness = [] } = config;
|
|
11172
11135
|
const prevout = normalize_prevout(config.prevout);
|
|
11173
11136
|
const sequence = normalize_sequence(config.sequence);
|
|
@@ -11199,16 +11162,14 @@ function create_tx(config) {
|
|
|
11199
11162
|
const WIT_FLAG_BYTES = 2;
|
|
11200
11163
|
function get_vsize(bytes) {
|
|
11201
11164
|
const weight = Buff.bytes(bytes).length;
|
|
11202
|
-
|
|
11203
|
-
return Math.ceil(weight / 4) + remain;
|
|
11165
|
+
return Math.ceil(weight / 4);
|
|
11204
11166
|
}
|
|
11205
11167
|
function get_txsize(txdata) {
|
|
11206
11168
|
const json = parse_tx(txdata);
|
|
11207
11169
|
const base = encode_tx(json, false).length;
|
|
11208
11170
|
const total = encode_tx(json, true).length;
|
|
11209
11171
|
const weight = base * 3 + total;
|
|
11210
|
-
const
|
|
11211
|
-
const vsize = Math.ceil(weight / 4) + remain;
|
|
11172
|
+
const vsize = Math.ceil(weight / 4);
|
|
11212
11173
|
return { base, total, vsize, weight };
|
|
11213
11174
|
}
|
|
11214
11175
|
function get_vin_size(vin) {
|
|
@@ -11286,18 +11247,18 @@ var index$4 = /*#__PURE__*/Object.freeze({
|
|
|
11286
11247
|
});
|
|
11287
11248
|
|
|
11288
11249
|
function get_prevout(vin) {
|
|
11289
|
-
Assert.exists(vin.prevout, `Prevout data missing for input: ${String(vin.txid)}`);
|
|
11250
|
+
Assert$1.exists(vin.prevout, `Prevout data missing for input: ${String(vin.txid)}`);
|
|
11290
11251
|
return vin.prevout;
|
|
11291
11252
|
}
|
|
11292
11253
|
function parse_txinput(txdata, config) {
|
|
11293
11254
|
let { txindex, txinput } = config ?? {};
|
|
11294
11255
|
if (txindex !== undefined) {
|
|
11295
11256
|
if (txindex >= txdata.vin.length) {
|
|
11296
|
-
throw new
|
|
11257
|
+
throw new ValidationError(`input index ${txindex} out of bounds. Transaction has ${txdata.vin.length} inputs (indices 0-${txdata.vin.length - 1})`);
|
|
11297
11258
|
}
|
|
11298
11259
|
txinput = txdata.vin.at(txindex);
|
|
11299
11260
|
}
|
|
11300
|
-
Assert.ok(txinput !== undefined);
|
|
11261
|
+
Assert$1.ok(txinput !== undefined);
|
|
11301
11262
|
return txinput;
|
|
11302
11263
|
}
|
|
11303
11264
|
function get_annex_data(witness) {
|
|
@@ -11319,14 +11280,16 @@ function hash_segwit_tx(txdata, options = {}) {
|
|
|
11319
11280
|
const is_anypay = (sigflag & 0x80) === 0x80;
|
|
11320
11281
|
const flag = sigflag % 0x80;
|
|
11321
11282
|
if (!SIGHASH_SEGWIT.includes(flag)) {
|
|
11322
|
-
throw new
|
|
11283
|
+
throw new ValidationError(`invalid sighash type: 0x${sigflag.toString(16)}. ` +
|
|
11284
|
+
`Valid values: SIGHASH_ALL (0x01), SIGHASH_NONE (0x02), SIGHASH_SINGLE (0x03), ` +
|
|
11285
|
+
`or combined with ANYONECANPAY (0x81, 0x82, 0x83)`);
|
|
11323
11286
|
}
|
|
11324
11287
|
const { version, vin, vout, locktime } = tx;
|
|
11325
11288
|
const txinput = parse_txinput(tx, options);
|
|
11326
11289
|
const { txid, vout: prevIdx, prevout, sequence } = txinput;
|
|
11327
11290
|
const { value } = prevout ?? {};
|
|
11328
11291
|
if (value === undefined) {
|
|
11329
|
-
throw new
|
|
11292
|
+
throw new ValidationError("Prevout value is required for segwit sighash calculation", "prevout.value");
|
|
11330
11293
|
}
|
|
11331
11294
|
let { pubkey, script } = options;
|
|
11332
11295
|
if (script === undefined && pubkey !== undefined) {
|
|
@@ -11334,10 +11297,10 @@ function hash_segwit_tx(txdata, options = {}) {
|
|
|
11334
11297
|
script = `76a914${String(pkhash)}88ac`;
|
|
11335
11298
|
}
|
|
11336
11299
|
if (script === undefined) {
|
|
11337
|
-
throw new
|
|
11300
|
+
throw new ValidationError("Either pubkey or script must be provided for segwit sighash", "pubkey/script");
|
|
11338
11301
|
}
|
|
11339
11302
|
if (decode_script(script).includes("OP_CODESEPARATOR")) {
|
|
11340
|
-
throw new
|
|
11303
|
+
throw new ValidationError("OP_CODESEPARATOR is not supported in segwit scripts", "script");
|
|
11341
11304
|
}
|
|
11342
11305
|
const sighash = [
|
|
11343
11306
|
encode_tx_version(version),
|
|
@@ -11385,8 +11348,8 @@ function bip143_hash_outputs(vout, sigflag, idx) {
|
|
|
11385
11348
|
return hash256(Buff.join(stack));
|
|
11386
11349
|
}
|
|
11387
11350
|
if (sigflag === 0x03) {
|
|
11388
|
-
Assert.ok(idx !== undefined, "txindex required for SIGHASH_SINGLE");
|
|
11389
|
-
Assert.ok(idx >= 0, "txindex must be non-negative");
|
|
11351
|
+
Assert$1.ok(idx !== undefined, "txindex required for SIGHASH_SINGLE");
|
|
11352
|
+
Assert$1.ok(idx >= 0, "txindex must be non-negative");
|
|
11390
11353
|
if (idx < vout.length) {
|
|
11391
11354
|
const { value, script_pk } = vout[idx];
|
|
11392
11355
|
stack.push(encode_vout_value(value));
|
|
@@ -11416,7 +11379,7 @@ function encode_leaf_version(version = 0xc0) {
|
|
|
11416
11379
|
return version & 0xfe;
|
|
11417
11380
|
}
|
|
11418
11381
|
function encode_taptweak(pubkey, data = new Uint8Array()) {
|
|
11419
|
-
Assert.
|
|
11382
|
+
Assert$1.ok(Buff.bytes(pubkey).length === 32, "pubkey must be 32 bytes");
|
|
11420
11383
|
return hash340("TapTweak", pubkey, data);
|
|
11421
11384
|
}
|
|
11422
11385
|
|
|
@@ -11431,10 +11394,12 @@ function get_taproot_tx_preimage(template, config = {}) {
|
|
|
11431
11394
|
const txinput = parse_txinput(tx, config);
|
|
11432
11395
|
const { txid, vout, sequence, witness = [] } = txinput;
|
|
11433
11396
|
if (!SIGHASH_TAPROOT.includes(sigflag)) {
|
|
11434
|
-
throw new
|
|
11397
|
+
throw new ValidationError(`invalid taproot sighash type: 0x${sigflag.toString(16)}. ` +
|
|
11398
|
+
`Valid values: SIGHASH_DEFAULT (0x00), SIGHASH_ALL (0x01), SIGHASH_NONE (0x02), SIGHASH_SINGLE (0x03), ` +
|
|
11399
|
+
`or combined with ANYONECANPAY (0x81, 0x82, 0x83)`);
|
|
11435
11400
|
}
|
|
11436
11401
|
if (extflag < 0 || extflag > 127) {
|
|
11437
|
-
throw new
|
|
11402
|
+
throw new ValidationError(`extension flag out of range: ${extflag}. Valid range is 0-127`);
|
|
11438
11403
|
}
|
|
11439
11404
|
let { extension } = config;
|
|
11440
11405
|
if (script !== undefined) {
|
|
@@ -11464,15 +11429,15 @@ function get_taproot_tx_preimage(template, config = {}) {
|
|
|
11464
11429
|
preimage.push(encode_txin_txid(txid), encode_txin_vout(vout), encode_vout_value(value), encode_script_data(script_pk), encode_txin_sequence(sequence));
|
|
11465
11430
|
}
|
|
11466
11431
|
else {
|
|
11467
|
-
Assert.ok(typeof txindex === "number");
|
|
11432
|
+
Assert$1.ok(typeof txindex === "number");
|
|
11468
11433
|
preimage.push(Buff.num(txindex, 4).reverse());
|
|
11469
11434
|
}
|
|
11470
11435
|
if (annex !== undefined) {
|
|
11471
11436
|
preimage.push(annex);
|
|
11472
11437
|
}
|
|
11473
11438
|
if ((sigflag & 0x03) === 0x03) {
|
|
11474
|
-
Assert.ok(typeof txindex === "number", "txindex required for SIGHASH_SINGLE");
|
|
11475
|
-
Assert.ok(txindex >= 0 && txindex < output.length, `txindex ${txindex} out of bounds for ${output.length} outputs`);
|
|
11439
|
+
Assert$1.ok(typeof txindex === "number", "txindex required for SIGHASH_SINGLE");
|
|
11440
|
+
Assert$1.ok(txindex >= 0 && txindex < output.length, `txindex ${txindex} out of bounds for ${output.length} outputs`);
|
|
11476
11441
|
preimage.push(bip341_hash_output(output[txindex]));
|
|
11477
11442
|
}
|
|
11478
11443
|
if (extension !== undefined) {
|
|
@@ -11531,31 +11496,31 @@ var index$3 = /*#__PURE__*/Object.freeze({
|
|
|
11531
11496
|
const SECKEY_REGEX = /^[0-9a-fA-F]{64}$/;
|
|
11532
11497
|
function validate_seckey(seckey) {
|
|
11533
11498
|
if (typeof seckey !== "string") {
|
|
11534
|
-
throw new
|
|
11499
|
+
throw new ValidationError("Secret key must be a string", "seckey");
|
|
11535
11500
|
}
|
|
11536
11501
|
if (!SECKEY_REGEX.test(seckey)) {
|
|
11537
|
-
throw new
|
|
11502
|
+
throw new ValidationError("Invalid secret key format: expected 32-byte hex string (64 characters)", "seckey");
|
|
11538
11503
|
}
|
|
11539
11504
|
}
|
|
11540
11505
|
function validate_sighash_options(options, validFlags) {
|
|
11541
11506
|
const { sigflag, txindex } = options;
|
|
11542
11507
|
if (sigflag !== undefined) {
|
|
11543
11508
|
if (typeof sigflag !== "number" || !Number.isInteger(sigflag)) {
|
|
11544
|
-
throw new
|
|
11509
|
+
throw new ConfigError("sigflag must be an integer");
|
|
11545
11510
|
}
|
|
11546
11511
|
const normalizedFlag = sigflag & 0x7f;
|
|
11547
11512
|
const isAnypay = (sigflag & 0x80) === 0x80;
|
|
11548
11513
|
const baseFlag = isAnypay ? normalizedFlag | 0x80 : normalizedFlag;
|
|
11549
11514
|
if (!validFlags.includes(baseFlag) &&
|
|
11550
11515
|
!validFlags.includes(normalizedFlag)) {
|
|
11551
|
-
throw new
|
|
11516
|
+
throw new ConfigError(`Invalid sigflag: ${sigflag}`);
|
|
11552
11517
|
}
|
|
11553
11518
|
}
|
|
11554
11519
|
if (txindex !== undefined) {
|
|
11555
11520
|
if (typeof txindex !== "number" ||
|
|
11556
11521
|
!Number.isInteger(txindex) ||
|
|
11557
11522
|
txindex < 0) {
|
|
11558
|
-
throw new
|
|
11523
|
+
throw new ValidationError("txindex must be a non-negative integer", "txindex");
|
|
11559
11524
|
}
|
|
11560
11525
|
}
|
|
11561
11526
|
}
|
|
@@ -11670,8 +11635,8 @@ function parse_witness_version(type) {
|
|
|
11670
11635
|
|
|
11671
11636
|
function parse_taproot_witness(witness) {
|
|
11672
11637
|
const { cblock, params, script } = parse_witness(witness);
|
|
11673
|
-
Assert.exists(cblock, "cblock is null");
|
|
11674
|
-
Assert.exists(script, "script is null");
|
|
11638
|
+
Assert$1.exists(cblock, "cblock is null");
|
|
11639
|
+
Assert$1.exists(script, "script is null");
|
|
11675
11640
|
const cblk = parse_cblock(cblock);
|
|
11676
11641
|
const target = encode_tapscript(script, cblk.version);
|
|
11677
11642
|
let branch = target.hex;
|
|
@@ -11680,8 +11645,8 @@ function parse_taproot_witness(witness) {
|
|
|
11680
11645
|
}
|
|
11681
11646
|
const tweak = encode_taptweak(cblk.int_key, branch);
|
|
11682
11647
|
const tapkey = tweak_pubkey(cblk.int_key, tweak, "bip340");
|
|
11683
|
-
params.map((e) => Buff.bytes(e).hex);
|
|
11684
|
-
return { cblock: cblk, params, script, tapkey: tapkey.hex, tweak: tweak.hex };
|
|
11648
|
+
const hexParams = params.map((e) => Buff.bytes(e).hex);
|
|
11649
|
+
return { cblock: cblk, params: hexParams, script, tapkey: tapkey.hex, tweak: tweak.hex };
|
|
11685
11650
|
}
|
|
11686
11651
|
function parse_cblock(cblock) {
|
|
11687
11652
|
const buffer = new Stream(cblock);
|
|
@@ -11693,21 +11658,21 @@ function parse_cblock(cblock) {
|
|
|
11693
11658
|
path.push(buffer.read(32).hex);
|
|
11694
11659
|
}
|
|
11695
11660
|
if (buffer.size !== 0) {
|
|
11696
|
-
throw new
|
|
11661
|
+
throw new DecodingError(`control block has ${buffer.size} extra bytes. Expected: 33 + (32 * path_length) bytes`);
|
|
11697
11662
|
}
|
|
11698
11663
|
return { int_key, path, parity, version };
|
|
11699
11664
|
}
|
|
11700
11665
|
function parse_cblock_parity(cbits) {
|
|
11701
|
-
return cbits % 2 === 0 ? [cbits
|
|
11666
|
+
return cbits % 2 === 0 ? [cbits, 0x02] : [cbits - 1, 0x03];
|
|
11702
11667
|
}
|
|
11703
11668
|
function parse_pubkey_parity(pubkey) {
|
|
11704
|
-
Assert.
|
|
11669
|
+
Assert$1.ok(Buff.bytes(pubkey).length === 33, "invalid pubkey size");
|
|
11705
11670
|
const [parity] = Buff.bytes(pubkey);
|
|
11706
11671
|
if (parity === 0x02)
|
|
11707
11672
|
return 0;
|
|
11708
11673
|
if (parity === 0x03)
|
|
11709
11674
|
return 1;
|
|
11710
|
-
throw new
|
|
11675
|
+
throw new ValidationError(`invalid pubkey parity prefix: 0x${parity.toString(16)}. Expected 0x02 (even) or 0x03 (odd)`);
|
|
11711
11676
|
}
|
|
11712
11677
|
|
|
11713
11678
|
const MAX_TAPROOT_DEPTH = 128;
|
|
@@ -11716,12 +11681,12 @@ function get_merkle_root(leaves) {
|
|
|
11716
11681
|
}
|
|
11717
11682
|
function merkleize(taptree, target, path = [], depth = 0) {
|
|
11718
11683
|
if (depth > MAX_TAPROOT_DEPTH) {
|
|
11719
|
-
throw new
|
|
11684
|
+
throw new ValidationError(`Taproot tree depth ${depth} exceeds maximum ${MAX_TAPROOT_DEPTH}`, "depth");
|
|
11720
11685
|
}
|
|
11721
11686
|
const leaves = [];
|
|
11722
11687
|
const tree = [];
|
|
11723
11688
|
if (taptree.length < 1) {
|
|
11724
|
-
throw new
|
|
11689
|
+
throw new ValidationError("Taproot tree cannot be empty", "taptree");
|
|
11725
11690
|
}
|
|
11726
11691
|
for (let i = 0; i < taptree.length; i++) {
|
|
11727
11692
|
const bytes = taptree[i];
|
|
@@ -11798,7 +11763,7 @@ function create_taproot(config$1) {
|
|
|
11798
11763
|
};
|
|
11799
11764
|
}
|
|
11800
11765
|
function verify_taproot(tapkey, target, cblock) {
|
|
11801
|
-
Assert.
|
|
11766
|
+
Assert$1.ok(Buff.bytes(tapkey).length === 32, "tapkey must be 32 bytes");
|
|
11802
11767
|
const { parity, path, int_key } = parse_cblock(cblock);
|
|
11803
11768
|
const ext_key = Buff.join([parity, tapkey]);
|
|
11804
11769
|
let branch = Buff.bytes(target).hex;
|
|
@@ -11826,7 +11791,7 @@ function verify_tx(txdata, options = {}) {
|
|
|
11826
11791
|
if (!result.valid) {
|
|
11827
11792
|
allValid = false;
|
|
11828
11793
|
if (throws) {
|
|
11829
|
-
throw new
|
|
11794
|
+
throw new ValidationError(`Input ${i} verification failed: ${result.error}`, `vin[${i}]`);
|
|
11830
11795
|
}
|
|
11831
11796
|
}
|
|
11832
11797
|
}
|
|
@@ -11851,12 +11816,11 @@ function verify_input(tx, vin, index, options) {
|
|
|
11851
11816
|
if (prevout === null || prevout === undefined) {
|
|
11852
11817
|
return { index, valid: false, type, error: "Missing prevout data" };
|
|
11853
11818
|
}
|
|
11854
|
-
const scriptType = get_lock_script_type(prevout.script_pk);
|
|
11855
11819
|
if (version === 0) {
|
|
11856
11820
|
return verify_segwit_input(tx, vin, index, witnessData);
|
|
11857
11821
|
}
|
|
11858
11822
|
else if (version === 1) {
|
|
11859
|
-
return verify_taproot_input(tx, vin, index, witnessData,
|
|
11823
|
+
return verify_taproot_input(tx, vin, index, witnessData, options);
|
|
11860
11824
|
}
|
|
11861
11825
|
return {
|
|
11862
11826
|
index,
|
|
@@ -11938,7 +11902,7 @@ function verify_segwit_input(tx, vin, index, witnessData, options) {
|
|
|
11938
11902
|
error: isValid ? undefined : "Invalid ECDSA signature",
|
|
11939
11903
|
};
|
|
11940
11904
|
}
|
|
11941
|
-
function verify_taproot_input(tx, vin, index, witnessData,
|
|
11905
|
+
function verify_taproot_input(tx, vin, index, witnessData, options) {
|
|
11942
11906
|
const { type, params, script, cblock } = witnessData;
|
|
11943
11907
|
if (vin.prevout == null) {
|
|
11944
11908
|
return {
|
|
@@ -12026,12 +11990,12 @@ function parse_schnorr_signature(sigHex) {
|
|
|
12026
11990
|
else if (sigBytes.length === 65) {
|
|
12027
11991
|
const sigflag = sigBytes.at(-1) ?? 0x00;
|
|
12028
11992
|
if (sigflag === 0x00) {
|
|
12029
|
-
throw new
|
|
11993
|
+
throw new ValidationError("0x00 is not a valid appended sigflag (use 64-byte signature for SIGHASH_DEFAULT)", "sigflag");
|
|
12030
11994
|
}
|
|
12031
11995
|
const signature = sigBytes.slice(0, 64).hex;
|
|
12032
11996
|
return { signature, sigflag };
|
|
12033
11997
|
}
|
|
12034
|
-
throw new
|
|
11998
|
+
throw new ValidationError(`Invalid Schnorr signature length: ${sigBytes.length} (expected 64 or 65 bytes)`, "signature");
|
|
12035
11999
|
}
|
|
12036
12000
|
|
|
12037
12001
|
var index$2 = /*#__PURE__*/Object.freeze({
|
|
@@ -12066,8 +12030,8 @@ function get_witness_size(witness) {
|
|
|
12066
12030
|
return { total: size, vsize };
|
|
12067
12031
|
}
|
|
12068
12032
|
function assert_witness(witness) {
|
|
12069
|
-
Assert.ok(Array.isArray(witness), "witness must be an array");
|
|
12070
|
-
Assert.ok(witness.every((e) => Buff.is_bytes(e)), "witness must be an array of strings or bytes");
|
|
12033
|
+
Assert$1.ok(Array.isArray(witness), "witness must be an array");
|
|
12034
|
+
Assert$1.ok(witness.every((e) => Buff.is_bytes(e)), "witness must be an array of strings or bytes");
|
|
12071
12035
|
}
|
|
12072
12036
|
|
|
12073
12037
|
var index = /*#__PURE__*/Object.freeze({
|
|
@@ -12079,6 +12043,8 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
12079
12043
|
|
|
12080
12044
|
exports.ADDRESS = index$8;
|
|
12081
12045
|
exports.CONST = _const;
|
|
12046
|
+
exports.ConfigError = ConfigError;
|
|
12047
|
+
exports.DecodingError = DecodingError;
|
|
12082
12048
|
exports.META = index$7;
|
|
12083
12049
|
exports.SCHEMA = index$5;
|
|
12084
12050
|
exports.SCRIPT = index$6;
|
|
@@ -12086,5 +12052,6 @@ exports.SIGHASH = index$3;
|
|
|
12086
12052
|
exports.SIGNER = index$2;
|
|
12087
12053
|
exports.TAPROOT = index$1;
|
|
12088
12054
|
exports.TX = index$4;
|
|
12055
|
+
exports.ValidationError = ValidationError;
|
|
12089
12056
|
exports.WITNESS = index;
|
|
12090
12057
|
//# sourceMappingURL=main.cjs.map
|