@scure/btc-signer 2.0.1 → 2.3.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/src/utils.ts CHANGED
@@ -1,34 +1,234 @@
1
1
  import { schnorr, secp256k1 as secp } from '@noble/curves/secp256k1.js';
2
- import { bytesToNumberBE, numberToBytesBE } from '@noble/curves/utils.js';
2
+ import { abytes, bytesToNumberBE, numberToBytesBE } from '@noble/curves/utils.js';
3
3
  import { ripemd160 } from '@noble/hashes/legacy.js';
4
- import { sha256 } from '@noble/hashes/sha2.js';
4
+ import { sha256 as nobleSha256 } from '@noble/hashes/sha2.js';
5
+ import { type TArg, type TRet } from '@noble/hashes/utils.js';
5
6
  import { utils as packedUtils, U32LE } from 'micro-packed';
7
+ export { abytes, validateObject as vld } from '@noble/curves/utils.js';
8
+ export { type TArg, type TRet } from '@noble/hashes/utils.js';
6
9
 
10
+ /** Hex-like input accepted by helpers in this module. */
7
11
  export type Hex = string | Uint8Array;
12
+ /** Byte array alias used across the library. */
8
13
  export type Bytes = Uint8Array;
9
- const Point = secp.Point;
10
- const Fn = Point.Fn;
11
- const CURVE_ORDER = Point.Fn.ORDER;
12
- export const hasEven = (y: bigint) => y % 2n === 0n;
13
-
14
- const isBytes: (a: unknown) => a is Uint8Array = packedUtils.isBytes;
15
- const concatBytes: (...arrays: Uint8Array[]) => Uint8Array = packedUtils.concatBytes;
16
- const equalBytes: (a: Uint8Array, b: Uint8Array) => boolean = packedUtils.equalBytes;
17
- export { concatBytes, equalBytes, isBytes, sha256 };
18
-
19
- export const hash160 = (msg: Uint8Array): Uint8Array => ripemd160(sha256(msg));
20
- export const sha256x2 = (...msgs: Uint8Array[]): Uint8Array => sha256(sha256(concatBytes(...msgs)));
21
- export const randomPrivateKeyBytes: () => Uint8Array = schnorr.utils.randomSecretKey;
22
- export const pubSchnorr = schnorr.getPublicKey as (priv: string | Uint8Array) => Uint8Array;
23
- export const pubECDSA: (privateKey: Uint8Array, isCompressed?: boolean) => Uint8Array =
24
- secp.getPublicKey;
14
+
15
+ /**
16
+ * Validates that a value is a non-negative bigint.
17
+ * @param n - Value to validate.
18
+ * @param title - Label included in thrown errors.
19
+ * @returns The same bigint.
20
+ * @throws On wrong argument types. {@link TypeError}
21
+ * @example
22
+ * Validate a satoshi amount before transaction encoding.
23
+ * ```ts
24
+ * abigint(1n, 'amount');
25
+ * ```
26
+ */
27
+ export function abigint(n: unknown, title: string = 'value'): bigint {
28
+ if (typeof n !== 'bigint')
29
+ throw new TypeError(`"${title}" expected bigint, got type=${typeof n}`);
30
+ if (n < _0n) throw new RangeError(`"${title}" expected non-negative bigint, got ${n}`);
31
+ return n;
32
+ }
33
+
34
+ import { validateObject as vld } from '@noble/curves/utils.js';
35
+
36
+ export function aarray<T>(
37
+ item: unknown,
38
+ title: string,
39
+ inner: (elm: T, title: string) => void = () => {}
40
+ ): T[] {
41
+ if (!Array.isArray(item))
42
+ throw new TypeError(`"${title}" expected array, got type=${typeof item}`);
43
+ for (let i = 0; i < item.length; i++) inner(item[i], `${title}[${i}]`);
44
+ return item;
45
+ }
46
+ /**
47
+ * Asserts something is a string.
48
+ * @param value - Value to validate.
49
+ * @param title - Label included in thrown errors.
50
+ * @returns The validated string.
51
+ * @throws On wrong argument types. {@link TypeError}
52
+ * @example
53
+ * Validate a label string.
54
+ *
55
+ * ```ts
56
+ * astring('example', 'label');
57
+ * ```
58
+ */
59
+ export function astring(value: unknown, title: string = ''): string {
60
+ if (typeof value !== 'string') {
61
+ const prefix = title && `"${title}" `;
62
+ throw new TypeError(prefix + 'expected string, got type=' + typeof value);
63
+ }
64
+ return value;
65
+ }
66
+ export function validateObject(
67
+ object: Record<string, any>,
68
+ fields: Record<string, string> = {},
69
+ optFields: Record<string, string> = {},
70
+ _title = 'object'
71
+ ) {
72
+ return vld(object, fields, optFields);
73
+ }
74
+ const Point = /* @__PURE__ */ (() => secp.Point)();
75
+ const Fn = /* @__PURE__ */ (() => Point.Fn)();
76
+ const CURVE_ORDER = /* @__PURE__ */ (() => Point.Fn.ORDER)();
77
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
78
+ // prettier-ignore
79
+ const _0n = /* @__PURE__ */ BigInt(0), _2n = /* @__PURE__ */ BigInt(2);
80
+ /**
81
+ * Checks whether a curve y-coordinate is even.
82
+ * @param y - y-coordinate to inspect
83
+ * @returns `true` when the coordinate is even.
84
+ * @example
85
+ * Check whether a point coordinate has even parity.
86
+ * ```ts
87
+ * hasEven(2n);
88
+ * ```
89
+ */
90
+ export const hasEven = (y: bigint) => y % _2n === _0n;
91
+
92
+ /**
93
+ * Checks whether a value is a Uint8Array.
94
+ * @param a - value to inspect
95
+ * @returns `true` when the value is a Uint8Array.
96
+ * @example
97
+ * Check whether an unknown value is already bytes.
98
+ * ```ts
99
+ * isBytes(new Uint8Array([1]));
100
+ * ```
101
+ */
102
+ export const isBytes: (a: unknown) => a is Uint8Array = /* @__PURE__ */ (() =>
103
+ packedUtils.isBytes)();
104
+ /**
105
+ * Concatenates byte arrays into a single Uint8Array.
106
+ * @param arrays - byte arrays to concatenate
107
+ * @returns Concatenated byte array.
108
+ * @example
109
+ * Join several byte chunks before hashing or signing them.
110
+ * ```ts
111
+ * concatBytes(new Uint8Array([1]), new Uint8Array([2]));
112
+ * ```
113
+ */
114
+ export const concatBytes: (...arrays: TArg<Uint8Array[]>) => TRet<Uint8Array> =
115
+ /* @__PURE__ */ (() =>
116
+ packedUtils.concatBytes as (...arrays: TArg<Uint8Array[]>) => TRet<Uint8Array>)();
117
+ /**
118
+ * Compares two byte arrays for equality.
119
+ * @param a - first byte array
120
+ * @param b - second byte array
121
+ * @returns `true` when both arrays contain the same bytes.
122
+ * @example
123
+ * Compare two serialized values without converting them first.
124
+ * ```ts
125
+ * equalBytes(new Uint8Array([1]), new Uint8Array([1]));
126
+ * ```
127
+ */
128
+ export const equalBytes: (a: TArg<Uint8Array>, b: TArg<Uint8Array>) => boolean =
129
+ /* @__PURE__ */ (() =>
130
+ packedUtils.equalBytes as (a: TArg<Uint8Array>, b: TArg<Uint8Array>) => boolean)();
131
+ /**
132
+ * SHA-256 hash function.
133
+ * @param msg - bytes to hash
134
+ * @returns SHA-256 digest.
135
+ * @example
136
+ * Hash a byte array with SHA-256.
137
+ * ```ts
138
+ * sha256(new Uint8Array([1, 2, 3]));
139
+ * ```
140
+ */
141
+ export const sha256: typeof nobleSha256 = /* @__PURE__ */ (() => nobleSha256)();
142
+
143
+ /**
144
+ * HASH160 helper used by classic Bitcoin addresses.
145
+ * @param msg - bytes to hash
146
+ * @returns RIPEMD160(SHA256(msg)).
147
+ * @example
148
+ * Derive the HASH160 used by legacy address formats.
149
+ * ```ts
150
+ * hash160(new Uint8Array([1, 2, 3]));
151
+ * ```
152
+ */
153
+ export const hash160 = (msg: TArg<Uint8Array>): TRet<Uint8Array> =>
154
+ ripemd160(sha256(msg)) as TRet<Uint8Array>;
155
+ /**
156
+ * Double-SHA256 helper used by Bitcoin transaction ids.
157
+ * @param msgs - message parts to concatenate and hash
158
+ * @returns SHA256(SHA256(concat(msgs))).
159
+ * @example
160
+ * Compute the double-SHA256 used by txids and sighashes.
161
+ * ```ts
162
+ * sha256x2(new Uint8Array([1]), new Uint8Array([2]));
163
+ * ```
164
+ */
165
+ export const sha256x2 = (...msgs: TArg<Uint8Array[]>): TRet<Uint8Array> =>
166
+ sha256(sha256(concatBytes(...msgs))) as TRet<Uint8Array>;
167
+ /**
168
+ * Generates a random secp256k1 private key.
169
+ * @returns Random 32-byte private key.
170
+ * @example
171
+ * Generate a fresh secp256k1 private key for signing.
172
+ * ```ts
173
+ * const privKey = randomPrivateKeyBytes();
174
+ * ```
175
+ */
176
+ export const randomPrivateKeyBytes = (): TRet<Uint8Array> =>
177
+ schnorr.utils.randomSecretKey() as TRet<Uint8Array>;
178
+ /**
179
+ * Derives a BIP340 Schnorr public key from a private key.
180
+ * @param priv - private key bytes
181
+ * @returns X-only public key bytes.
182
+ * @example
183
+ * Derive the x-only public key used by Schnorr and Taproot.
184
+ * ```ts
185
+ * import { pubSchnorr, randomPrivateKeyBytes } from '@scure/btc-signer/utils.js';
186
+ * pubSchnorr(randomPrivateKeyBytes());
187
+ * ```
188
+ */
189
+ export const pubSchnorr = (priv: TArg<Uint8Array>): TRet<Uint8Array> =>
190
+ schnorr.getPublicKey(priv) as TRet<Uint8Array>;
191
+ /**
192
+ * Derives a secp256k1 ECDSA public key from a private key.
193
+ * @param privateKey - private key bytes
194
+ * @param isCompressed - whether to return the compressed form
195
+ * @returns Serialized public key bytes.
196
+ * @example
197
+ * Derive the normal secp256k1 public key for legacy or SegWit scripts.
198
+ * ```ts
199
+ * import { pubECDSA, randomPrivateKeyBytes } from '@scure/btc-signer/utils.js';
200
+ * pubECDSA(randomPrivateKeyBytes());
201
+ * ```
202
+ */
203
+ export const pubECDSA = (privateKey: TArg<Uint8Array>, isCompressed?: boolean): TRet<Uint8Array> =>
204
+ secp.getPublicKey(privateKey, isCompressed) as TRet<Uint8Array>;
25
205
 
26
206
  // low-r signature grinding. Used to reduce tx size by 1 byte.
27
207
  // noble/secp256k1 does not support the feature: it is not used outside of BTC.
28
208
  // We implement it manually, because in BTC it's common.
29
209
  // Not best way, but closest to bitcoin implementation (easier to check)
30
- const hasLowR = (sig: { r: bigint; s: bigint }) => sig.r < CURVE_ORDER / 2n;
31
- export function signECDSA(hash: Bytes, privateKey: Bytes, lowR = false): Bytes {
210
+ // Hoisted: the bound is constant; no need to redo the bigint division on every
211
+ // grinding-loop iteration. n/2 < 2^255, so r < n/2 guarantees the 32-byte DER r.
212
+ const LOW_R_BOUND = /* @__PURE__ */ (() => CURVE_ORDER / _2n)();
213
+ const hasLowR = (sig: { r: bigint; s: bigint }) => sig.r < LOW_R_BOUND;
214
+ /**
215
+ * Signs a 32-byte hash with ECDSA and returns DER encoding.
216
+ * @param hash - message hash to sign
217
+ * @param privateKey - signer private key
218
+ * @param lowR - whether to grind for low-R signatures
219
+ * @returns DER-encoded signature bytes.
220
+ * @throws If low-R grinding overflows or ECDSA signing fails validation. {@link Error}
221
+ * @example
222
+ * Hash a message first, then create the DER-encoded ECDSA signature.
223
+ * ```ts
224
+ * import { randomPrivateKeyBytes, sha256, signECDSA } from '@scure/btc-signer/utils.js';
225
+ * signECDSA(sha256(new Uint8Array([1, 2, 3])), randomPrivateKeyBytes());
226
+ * ```
227
+ */
228
+ export function signECDSA(hash: TArg<Bytes>, privateKey: TArg<Bytes>, lowR = false): TRet<Bytes> {
229
+ // signECDSA is the 32-byte sighash wrapper for BTC callers, so reject arbitrary-length
230
+ // messages here instead of silently signing them with prehash disabled.
231
+ abytes(hash, 32, 'hash');
32
232
  let sig = secp.Signature.fromBytes(secp.sign(hash, privateKey, { prehash: false }));
33
233
  if (lowR && !hasLowR(sig)) {
34
234
  const extraEntropy = new Uint8Array(32);
@@ -39,34 +239,100 @@ export function signECDSA(hash: Bytes, privateKey: Bytes, lowR = false): Bytes {
39
239
  if (counter > 4294967295) throw new Error('lowR counter overflow: report the error');
40
240
  }
41
241
  }
42
- return sig.toBytes('der');
242
+ return sig.toBytes('der') as TRet<Bytes>;
43
243
  }
44
244
 
45
- export const signSchnorr: typeof schnorr.sign = schnorr.sign;
46
- export const tagSchnorr: typeof schnorr.utils.taggedHash = schnorr.utils.taggedHash;
245
+ /**
246
+ * BIP340 Schnorr signing function.
247
+ * @param message - 32-byte message digest
248
+ * @param secretKey - signer private key
249
+ * @param auxRand - optional auxiliary randomness
250
+ * @returns Schnorr signature bytes.
251
+ * @example
252
+ * Sign a 32-byte digest with the built-in BIP340 helper.
253
+ * ```ts
254
+ * import { randomPrivateKeyBytes, sha256, signSchnorr } from '@scure/btc-signer/utils.js';
255
+ * const msg = sha256(new Uint8Array([1, 2, 3]));
256
+ * signSchnorr(msg, randomPrivateKeyBytes());
257
+ * ```
258
+ */
259
+ export const signSchnorr = (
260
+ message: TArg<Uint8Array>,
261
+ secretKey: TArg<Uint8Array>,
262
+ auxRand?: TArg<Uint8Array>
263
+ ): TRet<Uint8Array> => schnorr.sign(message, secretKey, auxRand) as TRet<Uint8Array>;
264
+ /**
265
+ * Tagged-hash helper used by Schnorr and taproot constructions.
266
+ * @param tag - tagged-hash domain separator
267
+ * @param messages - message parts hashed under the tag
268
+ * @returns Tagged SHA-256 digest.
269
+ * @example
270
+ * Build the tagged hash used by Taproot leaves or tweaks.
271
+ * ```ts
272
+ * import { tagSchnorr } from '@scure/btc-signer/utils.js';
273
+ * tagSchnorr('TapLeaf', Uint8Array.of(0xc0), Uint8Array.of(0x51));
274
+ * ```
275
+ */
276
+ export const tagSchnorr = (tag: string, ...messages: TArg<Uint8Array[]>): TRet<Uint8Array> =>
277
+ schnorr.utils.taggedHash(tag, ...messages) as TRet<Uint8Array>;
47
278
 
48
- export const PubT = {
49
- ecdsa: 0,
50
- schnorr: 1,
51
- };
279
+ /** Public key format tags used by validation helpers. */
280
+ export const PubT = /* @__PURE__ */ (() =>
281
+ Object.freeze({
282
+ ecdsa: 0,
283
+ schnorr: 1,
284
+ }))();
285
+ /** Numeric public key format tag from {@link PubT}. */
52
286
  export type PubT = ValueOf<typeof PubT>;
53
287
 
54
- export function validatePubkey(pub: Bytes, type: PubT): Bytes {
288
+ /**
289
+ * Validates a public key against the expected Bitcoin key encoding.
290
+ * @param pub - public key bytes to validate
291
+ * @param type - expected public key format
292
+ * @returns The validated public key bytes.
293
+ * @throws On wrong argument types. {@link TypeError}
294
+ * @throws On wrong argument ranges or values. {@link RangeError}
295
+ * @example
296
+ * Reject keys that do not match the encoding required by the current script path.
297
+ * ```ts
298
+ * import {
299
+ * PubT,
300
+ * pubECDSA,
301
+ * randomPrivateKeyBytes,
302
+ * validatePubkey,
303
+ * } from '@scure/btc-signer/utils.js';
304
+ * validatePubkey(pubECDSA(randomPrivateKeyBytes()), PubT.ecdsa);
305
+ * ```
306
+ */
307
+ export function validatePubkey(pub: TArg<Bytes>, type: PubT): TRet<Bytes> {
55
308
  const len = pub.length;
56
309
  if (type === PubT.ecdsa) {
57
- if (len === 32) throw new Error('Expected non-Schnorr key');
310
+ if (len === 32) throw new RangeError('Expected non-Schnorr key');
58
311
  Point.fromBytes(pub); // does assertValidity
59
- return pub;
312
+ return pub as TRet<Bytes>;
60
313
  } else if (type === PubT.schnorr) {
61
- if (len !== 32) throw new Error('Expected 32-byte Schnorr key');
314
+ if (len !== 32) throw new RangeError('Expected 32-byte Schnorr key');
62
315
  schnorr.utils.lift_x(bytesToNumberBE(pub));
63
- return pub;
316
+ return pub as TRet<Bytes>;
64
317
  } else {
65
- throw new Error('Unknown key type');
318
+ throw new TypeError('Unknown key type');
66
319
  }
67
320
  }
68
321
 
69
- export function tapTweak(a: Bytes, b: Bytes): bigint {
322
+ /**
323
+ * Computes the Taproot tweak scalar from an internal key and merkle root.
324
+ * @param a - internal key bytes
325
+ * @param b - optional merkle root bytes
326
+ * @returns Taproot tweak scalar.
327
+ * @throws If the tweak scalar is outside the curve order. {@link Error}
328
+ * @example
329
+ * Combine the internal key and Merkle root into the Taproot tweak scalar.
330
+ * ```ts
331
+ * import { pubSchnorr, randomPrivateKeyBytes, tapTweak } from '@scure/btc-signer/utils.js';
332
+ * tapTweak(pubSchnorr(randomPrivateKeyBytes()), new Uint8Array());
333
+ * ```
334
+ */
335
+ export function tapTweak(a: TArg<Bytes>, b: TArg<Bytes>): bigint {
70
336
  const u = schnorr.utils;
71
337
  const t = u.taggedHash('TapTweak', a, b);
72
338
  const tn = bytesToNumberBE(t);
@@ -74,8 +340,27 @@ export function tapTweak(a: Bytes, b: Bytes): bigint {
74
340
  return tn;
75
341
  }
76
342
 
77
- export function taprootTweakPrivKey(privKey: Bytes, merkleRoot: Bytes = Uint8Array.of()): Bytes {
343
+ /**
344
+ * Tweaks a private key for Taproot key-path spending.
345
+ * @param privKey - internal private key bytes
346
+ * @param merkleRoot - optional taproot merkle root
347
+ * @returns Tweaked private key bytes.
348
+ * @throws If the Taproot tweak scalar is outside the curve order. {@link Error}
349
+ * @example
350
+ * Derive the tweaked Taproot key-path secret from the internal private key.
351
+ * ```ts
352
+ * import { randomPrivateKeyBytes, taprootTweakPrivKey } from '@scure/btc-signer/utils.js';
353
+ * taprootTweakPrivKey(randomPrivateKeyBytes());
354
+ * ```
355
+ */
356
+ export function taprootTweakPrivKey(
357
+ privKey: TArg<Bytes>,
358
+ merkleRoot: TArg<Bytes> = Uint8Array.of()
359
+ ): TRet<Bytes> {
78
360
  const u = schnorr.utils;
361
+ // BIP341 taproot_tweak_seckey starts with `seckey0 = int_from_bytes(seckey0)`, and
362
+ // BIP340 defines `int(x)` only for `x` as a 32-byte array, so reject other widths here.
363
+ abytes(privKey, 32, 'privKey');
79
364
  const seckey0 = bytesToNumberBE(privKey); // seckey0 = int_from_bytes(seckey0)
80
365
  const P = Point.BASE.multiply(seckey0); // P = point_mul(G, seckey0)
81
366
  // seckey = seckey0 if has_even_y(P) else SECP256K1_ORDER - seckey0
@@ -84,49 +369,95 @@ export function taprootTweakPrivKey(privKey: Bytes, merkleRoot: Bytes = Uint8Arr
84
369
  // t = int_from_bytes(tagged_hash("TapTweak", bytes_from_int(x(P)) + h)); >= SECP256K1_ORDER check
85
370
  const t = tapTweak(xP, merkleRoot);
86
371
  // bytes_from_int((seckey + t) % SECP256K1_ORDER)
87
- return numberToBytesBE(Fn.add(seckey, t), 32);
372
+ return numberToBytesBE(Fn.add(seckey, t), 32) as TRet<Bytes>;
88
373
  }
89
374
 
90
- export function taprootTweakPubkey(pubKey: Bytes, h: Bytes): [Bytes, number] {
375
+ /**
376
+ * Tweaks a Schnorr public key for Taproot key-path spending.
377
+ * @param pubKey - x-only internal public key
378
+ * @param h - taproot merkle root
379
+ * @returns Tweaked public key and output-key parity.
380
+ * @throws If the Taproot tweak scalar is outside the curve order. {@link Error}
381
+ * @example
382
+ * Derive the final Taproot output key from the internal key and Merkle root.
383
+ * ```ts
384
+ * import {
385
+ * pubSchnorr,
386
+ * randomPrivateKeyBytes,
387
+ * taprootTweakPubkey,
388
+ * } from '@scure/btc-signer/utils.js';
389
+ * taprootTweakPubkey(pubSchnorr(randomPrivateKeyBytes()), new Uint8Array());
390
+ * ```
391
+ */
392
+ export function taprootTweakPubkey(pubKey: TArg<Bytes>, h: TArg<Bytes>): TRet<[Bytes, number]> {
91
393
  const u = schnorr.utils;
394
+ // BIP341 taproot_tweak_pubkey feeds `pubkey` into `int_from_bytes(pubkey)`, and
395
+ // BIP340 defines `int(x)` only for `x` as a 32-byte array, so reject other widths here.
396
+ abytes(pubKey, 32, 'pubKey');
92
397
  const t = tapTweak(pubKey, h); // t = int_from_bytes(tagged_hash("TapTweak", pubkey + h))
93
398
  const P = u.lift_x(bytesToNumberBE(pubKey)); // P = lift_x(int_from_bytes(pubkey))
94
399
  const Q = P.add(Point.BASE.multiply(t)); // Q = point_add(P, point_mul(G, t))
95
400
  const parity = hasEven(Q.y) ? 0 : 1; // 0 if has_even_y(Q) else 1
96
- return [u.pointToBytes(Q), parity]; // bytes_from_int(x(Q))
401
+ return [u.pointToBytes(Q), parity] as TRet<[Bytes, number]>; // bytes_from_int(x(Q))
97
402
  }
98
403
 
99
404
  // Another stupid decision, where lack of standard affects security.
100
405
  // Multisig needs to be generated with some key.
101
- // We are using approach from BIP 341/bitcoinjs-lib: SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT))
406
+ // We are using the BIP 341/bitcoinjs-lib approach:
407
+ // SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT))
102
408
  // It is possible to switch SECP256K1_GENERATOR_POINT with some random point;
103
409
  // but it's too complex to prove.
104
410
  // Also used by bitcoin-core and bitcoinjs-lib
105
- export const TAPROOT_UNSPENDABLE_KEY: Bytes = sha256(Point.BASE.toBytes(false));
411
+ // This is the fixed BIP 341 H example, not the privacy-preserving H + rG variant.
412
+ // Downstream helpers use exact-byte equality with it to recognize
413
+ // library-generated script-only outputs.
414
+ /** Standard unspendable internal key used for script-only Taproot outputs. */
415
+ export const TAPROOT_UNSPENDABLE_KEY: TRet<Bytes> = /* @__PURE__ */ (() =>
416
+ sha256(Point.BASE.toBytes(false)) as TRet<Bytes>)();
106
417
 
418
+ /** Bitcoin network parameters. */
107
419
  export type BTC_NETWORK = {
420
+ /** Human-readable prefix used by Bech32 and Bech32m addresses. */
108
421
  bech32: string;
422
+ /** Base58 version byte for pay-to-public-key-hash addresses. */
109
423
  pubKeyHash: number;
424
+ /** Base58 version byte for pay-to-script-hash addresses. */
110
425
  scriptHash: number;
426
+ /** Base58 version byte for wallet-import-format private keys. */
111
427
  wif: number;
112
428
  };
113
- export const NETWORK: BTC_NETWORK = {
429
+ /** Bitcoin mainnet network parameters. */
430
+ export const NETWORK: BTC_NETWORK = /* @__PURE__ */ Object.freeze({
114
431
  bech32: 'bc',
115
432
  pubKeyHash: 0x00,
116
433
  scriptHash: 0x05,
117
434
  wif: 0x80,
118
- };
435
+ });
119
436
 
120
- export const TEST_NETWORK: BTC_NETWORK = {
437
+ /** Bitcoin testnet network parameters. */
438
+ export const TEST_NETWORK: BTC_NETWORK = /* @__PURE__ */ Object.freeze({
121
439
  bech32: 'tb',
122
440
  pubKeyHash: 0x6f,
123
441
  scriptHash: 0xc4,
124
442
  wif: 0xef,
125
- };
443
+ });
126
444
 
127
445
  // Exported for tests, internal method
128
- export function compareBytes(a: Bytes, b: Bytes): number {
129
- if (!isBytes(a) || !isBytes(b)) throw new Error(`cmp: wrong type a=${typeof a} b=${typeof b}`);
446
+ /**
447
+ * Lexicographically compares two byte arrays.
448
+ * @param a - first byte array
449
+ * @param b - second byte array
450
+ * @returns `-1`, `0`, or `1` depending on the ordering.
451
+ * @throws On wrong argument types. {@link TypeError}
452
+ * @example
453
+ * Compare two serialized keys using Bitcoin's byte ordering.
454
+ * ```ts
455
+ * compareBytes(new Uint8Array([1]), new Uint8Array([2]));
456
+ * ```
457
+ */
458
+ export function compareBytes(a: TArg<Bytes>, b: TArg<Bytes>): number {
459
+ if (!isBytes(a) || !isBytes(b))
460
+ throw new TypeError(`cmp: wrong type a=${typeof a} b=${typeof b}`);
130
461
  // -1 -> a<b, 0 -> a==b, 1 -> a>b
131
462
  const len = Math.min(a.length, b.length);
132
463
  for (let i = 0; i < len; i++) if (a[i] != b[i]) return Math.sign(a[i] - b[i]);
@@ -134,10 +465,23 @@ export function compareBytes(a: Bytes, b: Bytes): number {
134
465
  }
135
466
 
136
467
  // Reverses key<->values
468
+ /**
469
+ * Reverses an object's keys and values.
470
+ * @param obj - object to reverse
471
+ * @returns Object with original values mapped back to keys.
472
+ * @throws If duplicate values would collide while reversing the object. {@link Error}
473
+ * @example
474
+ * Flip a lookup table so the values become keys.
475
+ * ```ts
476
+ * reverseObject({ a: 1, b: 2 });
477
+ * ```
478
+ */
137
479
  export function reverseObject<T extends Record<string, string | number>>(
138
480
  obj: T
139
481
  ): { [K in T[keyof T]]: Extract<keyof T, string> } {
140
- const res = {} as any;
482
+ // Keep a raw dictionary shape so enum-like tables can reverse values like
483
+ // `toString` without colliding with inherited Object prototype properties.
484
+ const res = Object.create(null) as any;
141
485
  for (const k in obj) {
142
486
  if (res[obj[k]] !== undefined) throw new Error('duplicate key');
143
487
  res[obj[k]] = k;
@@ -145,4 +489,5 @@ export function reverseObject<T extends Record<string, string | number>>(
145
489
  return res;
146
490
  }
147
491
 
492
+ /** Union of all value types in an object type. */
148
493
  export type ValueOf<T> = T[keyof T];