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