@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/README.md +334 -64
- package/index.d.ts +15 -6
- package/index.js +16 -7
- package/musig2.d.ts +212 -69
- package/musig2.js +352 -99
- package/net.d.ts +355 -0
- package/net.js +875 -0
- package/p2p.d.ts +17 -8
- package/p2p.js +63 -11
- package/package.json +17 -17
- package/payment.d.ts +406 -41
- package/payment.js +570 -69
- package/psbt.d.ts +2958 -560
- package/psbt.js +475 -119
- package/script.d.ts +311 -133
- package/script.js +313 -90
- package/src/_type_test.ts +69 -0
- package/src/index.ts +34 -11
- package/src/musig2.ts +424 -155
- package/src/net.ts +1106 -0
- package/src/p2p.ts +76 -24
- package/src/payment.ts +882 -235
- package/src/psbt.ts +648 -229
- package/src/script.ts +397 -139
- package/src/transaction.ts +667 -196
- package/src/utils.ts +392 -47
- package/src/utxo.ts +182 -83
- package/transaction.d.ts +242 -32
- package/transaction.js +531 -121
- package/utils.d.ts +296 -25
- package/utils.js +337 -30
- package/utxo.d.ts +438 -76
- package/utxo.js +150 -59
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/musig2.d.ts.map +0 -1
- package/musig2.js.map +0 -1
- package/p2p.d.ts.map +0 -1
- package/p2p.js.map +0 -1
- package/payment.d.ts.map +0 -1
- package/payment.js.map +0 -1
- package/psbt.d.ts.map +0 -1
- package/psbt.js.map +0 -1
- package/script.d.ts.map +0 -1
- package/script.js.map +0 -1
- package/transaction.d.ts.map +0 -1
- package/transaction.js.map +0 -1
- package/utils.d.ts.map +0 -1
- package/utils.js.map +0 -1
- package/utxo.d.ts.map +0 -1
- package/utxo.js.map +0 -1
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
318
|
+
throw new TypeError('Unknown key type');
|
|
66
319
|
}
|
|
67
320
|
}
|
|
68
321
|
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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];
|