@scure/btc-signer 1.2.2 → 1.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.
Files changed (60) hide show
  1. package/README.md +19 -3
  2. package/lib/_type_test.d.ts +2 -0
  3. package/lib/_type_test.d.ts.map +1 -0
  4. package/lib/_type_test.js +59 -0
  5. package/lib/_type_test.js.map +1 -0
  6. package/lib/esm/_type_test.js +57 -0
  7. package/lib/esm/_type_test.js.map +1 -0
  8. package/lib/esm/index.js +13 -3007
  9. package/lib/esm/index.js.map +1 -1
  10. package/lib/esm/payment.js +681 -0
  11. package/lib/esm/payment.js.map +1 -0
  12. package/lib/esm/psbt.js +441 -0
  13. package/lib/esm/psbt.js.map +1 -0
  14. package/lib/esm/script.js +347 -0
  15. package/lib/esm/script.js.map +1 -0
  16. package/lib/esm/transaction.js +1013 -0
  17. package/lib/esm/transaction.js.map +1 -0
  18. package/lib/esm/utils.js +115 -0
  19. package/lib/esm/utils.js.map +1 -0
  20. package/lib/esm/utxo.js +491 -0
  21. package/lib/esm/utxo.js.map +1 -0
  22. package/lib/index.d.ts +18 -1439
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +53 -3042
  25. package/lib/index.js.map +1 -0
  26. package/lib/payment.d.ts +167 -0
  27. package/lib/payment.d.ts.map +1 -0
  28. package/lib/payment.js +704 -0
  29. package/lib/payment.js.map +1 -0
  30. package/lib/psbt.d.ts +834 -0
  31. package/lib/psbt.d.ts.map +1 -0
  32. package/lib/psbt.js +446 -0
  33. package/lib/psbt.js.map +1 -0
  34. package/lib/script.d.ts +154 -0
  35. package/lib/script.d.ts.map +1 -0
  36. package/lib/script.js +353 -0
  37. package/lib/script.js.map +1 -0
  38. package/lib/transaction.d.ts +223 -0
  39. package/lib/transaction.d.ts.map +1 -0
  40. package/lib/transaction.js +1022 -0
  41. package/lib/transaction.js.map +1 -0
  42. package/lib/utils.d.ts +30 -0
  43. package/lib/utils.d.ts.map +1 -0
  44. package/lib/utils.js +128 -0
  45. package/lib/utils.js.map +1 -0
  46. package/lib/utxo.d.ts +251 -0
  47. package/lib/utxo.d.ts.map +1 -0
  48. package/lib/utxo.js +501 -0
  49. package/lib/utxo.js.map +1 -0
  50. package/package.json +40 -10
  51. package/src/_type_test.ts +69 -0
  52. package/src/index.ts +28 -0
  53. package/src/package.json +3 -0
  54. package/src/payment.ts +749 -0
  55. package/src/psbt.ts +512 -0
  56. package/src/script.ts +236 -0
  57. package/src/transaction.ts +1065 -0
  58. package/src/utils.ts +118 -0
  59. package/src/utxo.ts +517 -0
  60. package/index.ts +0 -3067
package/lib/payment.js ADDED
@@ -0,0 +1,704 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Address = exports.WIF = exports.sortedMultisig = exports.multisig = exports._sortPubkeys = exports.getAddress = exports.p2tr_ms = exports.p2tr_pk = exports.p2tr_ns = exports.combinations = exports.p2tr = exports.tapLeafHash = exports.TAP_LEAF_VERSION = exports.taprootListToTree = exports.p2ms = exports.p2wpkh = exports.p2wsh = exports.p2sh = exports.p2pkh = exports.p2pk = exports.checkScript = exports.OutScript = void 0;
4
+ const base_1 = require("@scure/base");
5
+ const P = require("micro-packed");
6
+ const psbt_js_1 = require("./psbt.js");
7
+ const script_js_1 = require("./script.js");
8
+ const utils_js_1 = require("./utils.js");
9
+ const u = require("./utils.js");
10
+ function isValidPubkey(pub, type) {
11
+ try {
12
+ u.validatePubkey(pub, type);
13
+ return true;
14
+ }
15
+ catch (e) {
16
+ return false;
17
+ }
18
+ }
19
+ const OutPK = {
20
+ encode(from) {
21
+ if (from.length !== 2 ||
22
+ !u.isBytes(from[0]) ||
23
+ !isValidPubkey(from[0], u.PubT.ecdsa) ||
24
+ from[1] !== 'CHECKSIG')
25
+ return;
26
+ return { type: 'pk', pubkey: from[0] };
27
+ },
28
+ decode: (to) => (to.type === 'pk' ? [to.pubkey, 'CHECKSIG'] : undefined),
29
+ };
30
+ const OutPKH = {
31
+ encode(from) {
32
+ if (from.length !== 5 || from[0] !== 'DUP' || from[1] !== 'HASH160' || !u.isBytes(from[2]))
33
+ return;
34
+ if (from[3] !== 'EQUALVERIFY' || from[4] !== 'CHECKSIG')
35
+ return;
36
+ return { type: 'pkh', hash: from[2] };
37
+ },
38
+ decode: (to) => to.type === 'pkh' ? ['DUP', 'HASH160', to.hash, 'EQUALVERIFY', 'CHECKSIG'] : undefined,
39
+ };
40
+ const OutSH = {
41
+ encode(from) {
42
+ if (from.length !== 3 || from[0] !== 'HASH160' || !u.isBytes(from[1]) || from[2] !== 'EQUAL')
43
+ return;
44
+ return { type: 'sh', hash: from[1] };
45
+ },
46
+ decode: (to) => to.type === 'sh' ? ['HASH160', to.hash, 'EQUAL'] : undefined,
47
+ };
48
+ const OutWSH = {
49
+ encode(from) {
50
+ if (from.length !== 2 || from[0] !== 0 || !u.isBytes(from[1]))
51
+ return;
52
+ if (from[1].length !== 32)
53
+ return;
54
+ return { type: 'wsh', hash: from[1] };
55
+ },
56
+ decode: (to) => (to.type === 'wsh' ? [0, to.hash] : undefined),
57
+ };
58
+ const OutWPKH = {
59
+ encode(from) {
60
+ if (from.length !== 2 || from[0] !== 0 || !u.isBytes(from[1]))
61
+ return;
62
+ if (from[1].length !== 20)
63
+ return;
64
+ return { type: 'wpkh', hash: from[1] };
65
+ },
66
+ decode: (to) => (to.type === 'wpkh' ? [0, to.hash] : undefined),
67
+ };
68
+ const OutMS = {
69
+ encode(from) {
70
+ const last = from.length - 1;
71
+ if (from[last] !== 'CHECKMULTISIG')
72
+ return;
73
+ const m = from[0];
74
+ const n = from[last - 1];
75
+ if (typeof m !== 'number' || typeof n !== 'number')
76
+ return;
77
+ const pubkeys = from.slice(1, -2);
78
+ if (n !== pubkeys.length)
79
+ return;
80
+ for (const pub of pubkeys)
81
+ if (!u.isBytes(pub))
82
+ return;
83
+ return { type: 'ms', m, pubkeys: pubkeys }; // we don't need n, since it is the same as pubkeys
84
+ },
85
+ // checkmultisig(n, ..pubkeys, m)
86
+ decode: (to) => to.type === 'ms' ? [to.m, ...to.pubkeys, to.pubkeys.length, 'CHECKMULTISIG'] : undefined,
87
+ };
88
+ const OutTR = {
89
+ encode(from) {
90
+ if (from.length !== 2 || from[0] !== 1 || !u.isBytes(from[1]))
91
+ return;
92
+ return { type: 'tr', pubkey: from[1] };
93
+ },
94
+ decode: (to) => (to.type === 'tr' ? [1, to.pubkey] : undefined),
95
+ };
96
+ const OutTRNS = {
97
+ encode(from) {
98
+ const last = from.length - 1;
99
+ if (from[last] !== 'CHECKSIG')
100
+ return;
101
+ const pubkeys = [];
102
+ // On error return, since it can be different script
103
+ for (let i = 0; i < last; i++) {
104
+ const elm = from[i];
105
+ if (i & 1) {
106
+ if (elm !== 'CHECKSIGVERIFY' || i === last - 1)
107
+ return;
108
+ continue;
109
+ }
110
+ if (!u.isBytes(elm))
111
+ return;
112
+ pubkeys.push(elm);
113
+ }
114
+ return { type: 'tr_ns', pubkeys };
115
+ },
116
+ decode: (to) => {
117
+ if (to.type !== 'tr_ns')
118
+ return;
119
+ const out = [];
120
+ for (let i = 0; i < to.pubkeys.length - 1; i++)
121
+ out.push(to.pubkeys[i], 'CHECKSIGVERIFY');
122
+ out.push(to.pubkeys[to.pubkeys.length - 1], 'CHECKSIG');
123
+ return out;
124
+ },
125
+ };
126
+ const OutTRMS = {
127
+ encode(from) {
128
+ const last = from.length - 1;
129
+ if (from[last] !== 'NUMEQUAL' || from[1] !== 'CHECKSIG')
130
+ return;
131
+ const pubkeys = [];
132
+ const m = (0, script_js_1.OpToNum)(from[last - 1]);
133
+ if (typeof m !== 'number')
134
+ return;
135
+ for (let i = 0; i < last - 1; i++) {
136
+ const elm = from[i];
137
+ if (i & 1) {
138
+ if (elm !== (i === 1 ? 'CHECKSIG' : 'CHECKSIGADD'))
139
+ throw new Error('OutScript.encode/tr_ms: wrong element');
140
+ continue;
141
+ }
142
+ if (!u.isBytes(elm))
143
+ throw new Error('OutScript.encode/tr_ms: wrong key element');
144
+ pubkeys.push(elm);
145
+ }
146
+ return { type: 'tr_ms', pubkeys, m };
147
+ },
148
+ decode: (to) => {
149
+ if (to.type !== 'tr_ms')
150
+ return;
151
+ const out = [to.pubkeys[0], 'CHECKSIG'];
152
+ for (let i = 1; i < to.pubkeys.length; i++)
153
+ out.push(to.pubkeys[i], 'CHECKSIGADD');
154
+ out.push(to.m, 'NUMEQUAL');
155
+ return out;
156
+ },
157
+ };
158
+ const OutUnknown = {
159
+ encode(from) {
160
+ return { type: 'unknown', script: script_js_1.Script.encode(from) };
161
+ },
162
+ decode: (to) => to.type === 'unknown' ? script_js_1.Script.decode(to.script) : undefined,
163
+ };
164
+ // /Payments
165
+ const OutScripts = [
166
+ OutPK,
167
+ OutPKH,
168
+ OutSH,
169
+ OutWSH,
170
+ OutWPKH,
171
+ OutMS,
172
+ OutTR,
173
+ OutTRNS,
174
+ OutTRMS,
175
+ OutUnknown,
176
+ ];
177
+ // TODO: we can support user supplied output scripts now
178
+ // - addOutScript
179
+ // - removeOutScript
180
+ // - We can do that as log we modify array in-place
181
+ // - Actually is very hard, since there is sign/finalize logic
182
+ const _OutScript = P.apply(script_js_1.Script, P.coders.match(OutScripts));
183
+ // We can validate this once, because of packed & coders
184
+ exports.OutScript = P.validate(_OutScript, (i) => {
185
+ if (i.type === 'pk' && !isValidPubkey(i.pubkey, u.PubT.ecdsa))
186
+ throw new Error('OutScript/pk: wrong key');
187
+ if ((i.type === 'pkh' || i.type === 'sh' || i.type === 'wpkh') &&
188
+ (!u.isBytes(i.hash) || i.hash.length !== 20))
189
+ throw new Error(`OutScript/${i.type}: wrong hash`);
190
+ if (i.type === 'wsh' && (!u.isBytes(i.hash) || i.hash.length !== 32))
191
+ throw new Error(`OutScript/wsh: wrong hash`);
192
+ if (i.type === 'tr' && (!u.isBytes(i.pubkey) || !isValidPubkey(i.pubkey, u.PubT.schnorr)))
193
+ throw new Error('OutScript/tr: wrong taproot public key');
194
+ if (i.type === 'ms' || i.type === 'tr_ns' || i.type === 'tr_ms')
195
+ if (!Array.isArray(i.pubkeys))
196
+ throw new Error('OutScript/multisig: wrong pubkeys array');
197
+ if (i.type === 'ms') {
198
+ const n = i.pubkeys.length;
199
+ for (const p of i.pubkeys)
200
+ if (!isValidPubkey(p, u.PubT.ecdsa))
201
+ throw new Error('OutScript/multisig: wrong pubkey');
202
+ if (i.m <= 0 || n > 16 || i.m > n)
203
+ throw new Error('OutScript/multisig: invalid params');
204
+ }
205
+ if (i.type === 'tr_ns' || i.type === 'tr_ms') {
206
+ for (const p of i.pubkeys)
207
+ if (!isValidPubkey(p, u.PubT.schnorr))
208
+ throw new Error(`OutScript/${i.type}: wrong pubkey`);
209
+ }
210
+ if (i.type === 'tr_ms') {
211
+ const n = i.pubkeys.length;
212
+ if (i.m <= 0 || n > 999 || i.m > n)
213
+ throw new Error('OutScript/tr_ms: invalid params');
214
+ }
215
+ return i;
216
+ });
217
+ // Basic sanity check for scripts
218
+ function checkWSH(s, witnessScript) {
219
+ if (!P.equalBytes(s.hash, u.sha256(witnessScript)))
220
+ throw new Error('checkScript: wsh wrong witnessScript hash');
221
+ const w = exports.OutScript.decode(witnessScript);
222
+ if (w.type === 'tr' || w.type === 'tr_ns' || w.type === 'tr_ms')
223
+ throw new Error(`checkScript: P2${w.type} cannot be wrapped in P2SH`);
224
+ if (w.type === 'wpkh' || w.type === 'sh')
225
+ throw new Error(`checkScript: P2${w.type} cannot be wrapped in P2WSH`);
226
+ }
227
+ function checkScript(script, redeemScript, witnessScript) {
228
+ if (script) {
229
+ const s = exports.OutScript.decode(script);
230
+ // ms||pk maybe work, but there will be no address, hard to spend
231
+ if (s.type === 'tr_ns' || s.type === 'tr_ms' || s.type === 'ms' || s.type == 'pk')
232
+ throw new Error(`checkScript: non-wrapped ${s.type}`);
233
+ if (s.type === 'sh' && redeemScript) {
234
+ if (!P.equalBytes(s.hash, u.hash160(redeemScript)))
235
+ throw new Error('checkScript: sh wrong redeemScript hash');
236
+ const r = exports.OutScript.decode(redeemScript);
237
+ if (r.type === 'tr' || r.type === 'tr_ns' || r.type === 'tr_ms')
238
+ throw new Error(`checkScript: P2${r.type} cannot be wrapped in P2SH`);
239
+ // Not sure if this unspendable, but we cannot represent this via PSBT
240
+ if (r.type === 'sh')
241
+ throw new Error('checkScript: P2SH cannot be wrapped in P2SH');
242
+ }
243
+ if (s.type === 'wsh' && witnessScript)
244
+ checkWSH(s, witnessScript);
245
+ }
246
+ if (redeemScript) {
247
+ const r = exports.OutScript.decode(redeemScript);
248
+ if (r.type === 'wsh' && witnessScript)
249
+ checkWSH(r, witnessScript);
250
+ }
251
+ }
252
+ exports.checkScript = checkScript;
253
+ function uniqPubkey(pubkeys) {
254
+ const map = {};
255
+ for (const pub of pubkeys) {
256
+ const key = base_1.hex.encode(pub);
257
+ if (map[key])
258
+ throw new Error(`Multisig: non-uniq pubkey: ${pubkeys.map(base_1.hex.encode)}`);
259
+ map[key] = true;
260
+ }
261
+ }
262
+ // @ts-ignore
263
+ const p2pk = (pubkey, network = utils_js_1.NETWORK) => {
264
+ // network is unused
265
+ if (!isValidPubkey(pubkey, u.PubT.ecdsa))
266
+ throw new Error('P2PK: invalid publicKey');
267
+ return {
268
+ type: 'pk',
269
+ script: exports.OutScript.encode({ type: 'pk', pubkey }),
270
+ };
271
+ };
272
+ exports.p2pk = p2pk;
273
+ const p2pkh = (publicKey, network = utils_js_1.NETWORK) => {
274
+ if (!isValidPubkey(publicKey, u.PubT.ecdsa))
275
+ throw new Error('P2PKH: invalid publicKey');
276
+ const hash = u.hash160(publicKey);
277
+ return {
278
+ type: 'pkh',
279
+ script: exports.OutScript.encode({ type: 'pkh', hash }),
280
+ address: Address(network).encode({ type: 'pkh', hash }),
281
+ };
282
+ };
283
+ exports.p2pkh = p2pkh;
284
+ const p2sh = (child, network = utils_js_1.NETWORK) => {
285
+ // It is already tested inside noble-hashes and checkScript
286
+ const cs = child.script;
287
+ if (!u.isBytes(cs))
288
+ throw new Error(`Wrong script: ${typeof child.script}, expected Uint8Array`);
289
+ const hash = u.hash160(cs);
290
+ const script = exports.OutScript.encode({ type: 'sh', hash });
291
+ checkScript(script, cs, child.witnessScript);
292
+ const res = {
293
+ type: 'sh',
294
+ redeemScript: cs,
295
+ script: exports.OutScript.encode({ type: 'sh', hash }),
296
+ address: Address(network).encode({ type: 'sh', hash }),
297
+ };
298
+ if (child.witnessScript)
299
+ res.witnessScript = child.witnessScript;
300
+ return res;
301
+ };
302
+ exports.p2sh = p2sh;
303
+ const p2wsh = (child, network = utils_js_1.NETWORK) => {
304
+ const cs = child.script;
305
+ if (!u.isBytes(cs))
306
+ throw new Error(`Wrong script: ${typeof cs}, expected Uint8Array`);
307
+ const hash = u.sha256(cs);
308
+ const script = exports.OutScript.encode({ type: 'wsh', hash });
309
+ checkScript(script, undefined, cs);
310
+ return {
311
+ type: 'wsh',
312
+ witnessScript: cs,
313
+ script: exports.OutScript.encode({ type: 'wsh', hash }),
314
+ address: Address(network).encode({ type: 'wsh', hash }),
315
+ };
316
+ };
317
+ exports.p2wsh = p2wsh;
318
+ const p2wpkh = (publicKey, network = utils_js_1.NETWORK) => {
319
+ if (!isValidPubkey(publicKey, u.PubT.ecdsa))
320
+ throw new Error('P2WPKH: invalid publicKey');
321
+ if (publicKey.length === 65)
322
+ throw new Error('P2WPKH: uncompressed public key');
323
+ const hash = u.hash160(publicKey);
324
+ return {
325
+ type: 'wpkh',
326
+ script: exports.OutScript.encode({ type: 'wpkh', hash }),
327
+ address: Address(network).encode({ type: 'wpkh', hash }),
328
+ };
329
+ };
330
+ exports.p2wpkh = p2wpkh;
331
+ const p2ms = (m, pubkeys, allowSamePubkeys = false) => {
332
+ if (!allowSamePubkeys)
333
+ uniqPubkey(pubkeys);
334
+ return { type: 'ms', script: exports.OutScript.encode({ type: 'ms', pubkeys, m }) };
335
+ };
336
+ exports.p2ms = p2ms;
337
+ function checkTaprootScript(script, internalPubKey, allowUnknownOutputs = false, customScripts) {
338
+ const out = exports.OutScript.decode(script);
339
+ if (out.type === 'unknown') {
340
+ // NOTE: this check should be before allowUnknownOutputs, otherwise it will
341
+ // disable custom. All custom scripts for taproot should have prefix 'tr_'
342
+ if (customScripts) {
343
+ const cs = P.apply(script_js_1.Script, P.coders.match(customScripts));
344
+ const c = cs.decode(script);
345
+ if (c !== undefined) {
346
+ if (typeof c.type !== 'string' || !c.type.startsWith('tr_'))
347
+ throw new Error(`P2TR: invalid custom type=${c.type}`);
348
+ return;
349
+ }
350
+ }
351
+ if (allowUnknownOutputs)
352
+ return;
353
+ }
354
+ if (!['tr_ns', 'tr_ms'].includes(out.type))
355
+ throw new Error(`P2TR: invalid leaf script=${out.type}`);
356
+ const outms = out;
357
+ if (!allowUnknownOutputs && outms.pubkeys) {
358
+ for (const p of outms.pubkeys) {
359
+ if (P.equalBytes(p, u.TAPROOT_UNSPENDABLE_KEY))
360
+ throw new Error('Unspendable taproot key in leaf script');
361
+ // It's likely a mistake at this point:
362
+ // 1. p2tr(A, p2tr_ns(2, [A, B])) == p2tr(A, p2tr_pk(B)) (A or B key)
363
+ // but will take more space and fees.
364
+ // 2. For multi-sig p2tr(A, p2tr_ns(2, [A, B, C])) it's probably a security issue:
365
+ // User creates 2 of 3 multisig of keys [A, B, C],
366
+ // but key A always can spend whole output without signatures from other keys.
367
+ // p2tr(A, p2tr_ns(2, [B, C, D])) is ok: A or (B and C) or (B and D) or (C and D)
368
+ if (P.equalBytes(p, internalPubKey)) {
369
+ throw new Error('Using P2TR with leaf script with same key as internal key is not supported');
370
+ }
371
+ }
372
+ }
373
+ }
374
+ // Helper for generating binary tree from list, with weights
375
+ function taprootListToTree(taprootList) {
376
+ // Clone input in order to not corrupt it
377
+ const lst = Array.from(taprootList);
378
+ // We have at least 2 elements => can create branch
379
+ while (lst.length >= 2) {
380
+ // Sort: elements with smallest weight are in the end of queue
381
+ lst.sort((a, b) => (b.weight || 1) - (a.weight || 1));
382
+ const b = lst.pop();
383
+ const a = lst.pop();
384
+ const weight = (a?.weight || 1) + (b?.weight || 1);
385
+ lst.push({
386
+ weight,
387
+ // Unwrap children array
388
+ // TODO: Very hard to remove any here
389
+ childs: [a?.childs || a, b?.childs || b],
390
+ });
391
+ }
392
+ // At this point there is always 1 element in lst
393
+ const last = lst[0];
394
+ return (last?.childs || last);
395
+ }
396
+ exports.taprootListToTree = taprootListToTree;
397
+ function taprootAddPath(tree, path = []) {
398
+ if (!tree)
399
+ throw new Error(`taprootAddPath: empty tree`);
400
+ if (tree.type === 'leaf')
401
+ return { ...tree, path };
402
+ if (tree.type !== 'branch')
403
+ throw new Error(`taprootAddPath: wrong type=${tree}`);
404
+ return {
405
+ ...tree,
406
+ path,
407
+ // Left element has right hash in path and otherwise
408
+ left: taprootAddPath(tree.left, [tree.right.hash, ...path]),
409
+ right: taprootAddPath(tree.right, [tree.left.hash, ...path]),
410
+ };
411
+ }
412
+ function taprootWalkTree(tree) {
413
+ if (!tree)
414
+ throw new Error(`taprootAddPath: empty tree`);
415
+ if (tree.type === 'leaf')
416
+ return [tree];
417
+ if (tree.type !== 'branch')
418
+ throw new Error(`taprootWalkTree: wrong type=${tree}`);
419
+ return [...taprootWalkTree(tree.left), ...taprootWalkTree(tree.right)];
420
+ }
421
+ function taprootHashTree(tree, internalPubKey, allowUnknownOutputs = false, customScripts) {
422
+ if (!tree)
423
+ throw new Error('taprootHashTree: empty tree');
424
+ if (Array.isArray(tree) && tree.length === 1)
425
+ tree = tree[0];
426
+ // Terminal node (leaf)
427
+ if (!Array.isArray(tree)) {
428
+ const { leafVersion: version, script: leafScript } = tree;
429
+ // Earliest tree walk where we can validate tapScripts
430
+ if (tree.tapLeafScript || (tree.tapMerkleRoot && !P.equalBytes(tree.tapMerkleRoot, P.EMPTY)))
431
+ throw new Error('P2TR: tapRoot leafScript cannot have tree');
432
+ const script = typeof leafScript === 'string' ? base_1.hex.decode(leafScript) : leafScript;
433
+ if (!u.isBytes(script))
434
+ throw new Error(`checkScript: wrong script type=${script}`);
435
+ checkTaprootScript(script, internalPubKey, allowUnknownOutputs, customScripts);
436
+ return {
437
+ type: 'leaf',
438
+ version,
439
+ script,
440
+ hash: (0, exports.tapLeafHash)(script, version),
441
+ };
442
+ }
443
+ // If tree / branch is not binary tree, convert it
444
+ if (tree.length !== 2)
445
+ tree = taprootListToTree(tree);
446
+ if (tree.length !== 2)
447
+ throw new Error('hashTree: non binary tree!');
448
+ // branch
449
+ // Both nodes should exist
450
+ const left = taprootHashTree(tree[0], internalPubKey, allowUnknownOutputs, customScripts);
451
+ const right = taprootHashTree(tree[1], internalPubKey, allowUnknownOutputs, customScripts);
452
+ // We cannot swap left/right here, since it will change structure of tree
453
+ let [lH, rH] = [left.hash, right.hash];
454
+ if (u.compareBytes(rH, lH) === -1)
455
+ [lH, rH] = [rH, lH];
456
+ return { type: 'branch', left, right, hash: u.tagSchnorr('TapBranch', lH, rH) };
457
+ }
458
+ exports.TAP_LEAF_VERSION = 0xc0;
459
+ const tapLeafHash = (script, version = exports.TAP_LEAF_VERSION) => u.tagSchnorr('TapLeaf', new Uint8Array([version]), script_js_1.VarBytes.encode(script));
460
+ exports.tapLeafHash = tapLeafHash;
461
+ // Works as key OR tree.
462
+ // If we only have tree, need to add unspendable key, otherwise
463
+ // complex multisig wallet can be spent by owner of key only. See TAPROOT_UNSPENDABLE_KEY
464
+ function p2tr(internalPubKey, tree, network = utils_js_1.NETWORK, allowUnknownOutputs = false, customScripts) {
465
+ // Unspendable
466
+ if (!internalPubKey && !tree)
467
+ throw new Error('p2tr: should have pubKey or scriptTree (or both)');
468
+ const pubKey = typeof internalPubKey === 'string'
469
+ ? base_1.hex.decode(internalPubKey)
470
+ : internalPubKey || u.TAPROOT_UNSPENDABLE_KEY;
471
+ if (!isValidPubkey(pubKey, u.PubT.schnorr))
472
+ throw new Error('p2tr: non-schnorr pubkey');
473
+ let hashedTree = tree
474
+ ? taprootAddPath(taprootHashTree(tree, pubKey, allowUnknownOutputs, customScripts))
475
+ : undefined;
476
+ const tapMerkleRoot = hashedTree ? hashedTree.hash : undefined;
477
+ const [tweakedPubkey, parity] = u.taprootTweakPubkey(pubKey, tapMerkleRoot || P.EMPTY);
478
+ let leaves;
479
+ if (hashedTree) {
480
+ leaves = taprootWalkTree(hashedTree).map((l) => ({
481
+ ...l,
482
+ controlBlock: psbt_js_1.TaprootControlBlock.encode({
483
+ version: (l.version || exports.TAP_LEAF_VERSION) + parity,
484
+ internalKey: pubKey,
485
+ merklePath: l.path,
486
+ }),
487
+ }));
488
+ }
489
+ let tapLeafScript;
490
+ if (leaves) {
491
+ tapLeafScript = leaves.map((l) => [
492
+ psbt_js_1.TaprootControlBlock.decode(l.controlBlock),
493
+ u.concatBytes(l.script, new Uint8Array([l.version || exports.TAP_LEAF_VERSION])),
494
+ ]);
495
+ }
496
+ const res = {
497
+ type: 'tr',
498
+ script: exports.OutScript.encode({ type: 'tr', pubkey: tweakedPubkey }),
499
+ address: Address(network).encode({ type: 'tr', pubkey: tweakedPubkey }),
500
+ // For tests
501
+ tweakedPubkey,
502
+ // PSBT stuff
503
+ tapInternalKey: pubKey,
504
+ };
505
+ // Just in case someone would want to select a specific script
506
+ if (leaves)
507
+ res.leaves = leaves;
508
+ if (tapLeafScript)
509
+ res.tapLeafScript = tapLeafScript;
510
+ if (tapMerkleRoot)
511
+ res.tapMerkleRoot = tapMerkleRoot;
512
+ return res;
513
+ }
514
+ exports.p2tr = p2tr;
515
+ // Returns all combinations of size M from lst
516
+ function combinations(m, list) {
517
+ const res = [];
518
+ if (!Array.isArray(list))
519
+ throw new Error('combinations: lst arg should be array');
520
+ const n = list.length;
521
+ if (m > n)
522
+ throw new Error('combinations: m > lst.length, no combinations possible');
523
+ /*
524
+ Basically works as M nested loops like:
525
+ for (;idx[0]<lst.length;idx[0]++) for (idx[1]=idx[0]+1;idx[1]<lst.length;idx[1]++)
526
+ but since we cannot create nested loops dynamically, we unroll it to a single loop
527
+ */
528
+ const idx = Array.from({ length: m }, (_, i) => i);
529
+ const last = idx.length - 1;
530
+ main: for (;;) {
531
+ res.push(idx.map((i) => list[i]));
532
+ idx[last] += 1;
533
+ let i = last;
534
+ // Propagate increment
535
+ // idx[i] cannot be bigger than n-m+i, otherwise last elements in right part will overflow
536
+ for (; i >= 0 && idx[i] > n - m + i; i--) {
537
+ idx[i] = 0;
538
+ // Overflow in idx[0], break
539
+ if (i === 0)
540
+ break main;
541
+ idx[i - 1] += 1;
542
+ }
543
+ // Propagate: idx[i+1] = idx[idx]+1
544
+ for (i += 1; i < idx.length; i++)
545
+ idx[i] = idx[i - 1] + 1;
546
+ }
547
+ return res;
548
+ }
549
+ exports.combinations = combinations;
550
+ /**
551
+ * M-of-N multi-leaf wallet via p2tr_ns. If m == n, single script is emitted.
552
+ * Takes O(n^2) if m != n. 99-of-100 is ok, 5-of-100 is not.
553
+ * `2-of-[A,B,C] => [A,B] | [A,C] | [B,C]`
554
+ */
555
+ const p2tr_ns = (m, pubkeys, allowSamePubkeys = false) => {
556
+ if (!allowSamePubkeys)
557
+ uniqPubkey(pubkeys);
558
+ return combinations(m, pubkeys).map((i) => ({
559
+ type: 'tr_ns',
560
+ script: exports.OutScript.encode({ type: 'tr_ns', pubkeys: i }),
561
+ }));
562
+ };
563
+ exports.p2tr_ns = p2tr_ns;
564
+ // Taproot public key (case of p2tr_ns)
565
+ const p2tr_pk = (pubkey) => (0, exports.p2tr_ns)(1, [pubkey], undefined)[0];
566
+ exports.p2tr_pk = p2tr_pk;
567
+ function p2tr_ms(m, pubkeys, allowSamePubkeys = false) {
568
+ if (!allowSamePubkeys)
569
+ uniqPubkey(pubkeys);
570
+ return {
571
+ type: 'tr_ms',
572
+ script: exports.OutScript.encode({ type: 'tr_ms', pubkeys, m }),
573
+ };
574
+ }
575
+ exports.p2tr_ms = p2tr_ms;
576
+ // Simple pubkey address, without complex scripts
577
+ function getAddress(type, privKey, network = utils_js_1.NETWORK) {
578
+ if (type === 'tr') {
579
+ return p2tr(u.pubSchnorr(privKey), undefined, network).address;
580
+ }
581
+ const pubKey = u.pubECDSA(privKey);
582
+ if (type === 'pkh')
583
+ return (0, exports.p2pkh)(pubKey, network).address;
584
+ if (type === 'wpkh')
585
+ return (0, exports.p2wpkh)(pubKey, network).address;
586
+ throw new Error(`getAddress: unknown type=${type}`);
587
+ }
588
+ exports.getAddress = getAddress;
589
+ const _sortPubkeys = (pubkeys) => Array.from(pubkeys).sort(u.compareBytes);
590
+ exports._sortPubkeys = _sortPubkeys;
591
+ function multisig(m, pubkeys, sorted = false, witness = false) {
592
+ const ms = (0, exports.p2ms)(m, sorted ? (0, exports._sortPubkeys)(pubkeys) : pubkeys);
593
+ return witness ? (0, exports.p2wsh)(ms) : (0, exports.p2sh)(ms);
594
+ }
595
+ exports.multisig = multisig;
596
+ function sortedMultisig(m, pubkeys, witness = false) {
597
+ return multisig(m, pubkeys, true, witness);
598
+ }
599
+ exports.sortedMultisig = sortedMultisig;
600
+ const base58check = (0, base_1.createBase58check)(u.sha256);
601
+ function validateWitness(version, data) {
602
+ if (data.length < 2 || data.length > 40)
603
+ throw new Error('Witness: invalid length');
604
+ if (version > 16)
605
+ throw new Error('Witness: invalid version');
606
+ if (version === 0 && !(data.length === 20 || data.length === 32))
607
+ throw new Error('Witness: invalid length for version');
608
+ }
609
+ function programToWitness(version, data, network = utils_js_1.NETWORK) {
610
+ validateWitness(version, data);
611
+ const coder = version === 0 ? base_1.bech32 : base_1.bech32m;
612
+ return coder.encode(network.bech32, [version].concat(coder.toWords(data)));
613
+ }
614
+ function formatKey(hashed, prefix) {
615
+ return base58check.encode(u.concatBytes(Uint8Array.from(prefix), hashed));
616
+ }
617
+ function WIF(network = utils_js_1.NETWORK) {
618
+ return {
619
+ encode(privKey) {
620
+ const compressed = u.concatBytes(privKey, new Uint8Array([0x01]));
621
+ return formatKey(compressed.subarray(0, 33), [network.wif]);
622
+ },
623
+ decode(wif) {
624
+ let parsed = base58check.decode(wif);
625
+ if (parsed[0] !== network.wif)
626
+ throw new Error('Wrong WIF prefix');
627
+ parsed = parsed.subarray(1);
628
+ // Check what it is. Compressed flag?
629
+ if (parsed.length !== 33)
630
+ throw new Error('Wrong WIF length');
631
+ if (parsed[32] !== 0x01)
632
+ throw new Error('Wrong WIF postfix');
633
+ return parsed.subarray(0, -1);
634
+ },
635
+ };
636
+ }
637
+ exports.WIF = WIF;
638
+ // Returns OutType, which can be used to create outscript
639
+ function Address(network = utils_js_1.NETWORK) {
640
+ return {
641
+ encode(from) {
642
+ const { type } = from;
643
+ if (type === 'wpkh')
644
+ return programToWitness(0, from.hash, network);
645
+ else if (type === 'wsh')
646
+ return programToWitness(0, from.hash, network);
647
+ else if (type === 'tr')
648
+ return programToWitness(1, from.pubkey, network);
649
+ else if (type === 'pkh')
650
+ return formatKey(from.hash, [network.pubKeyHash]);
651
+ else if (type === 'sh')
652
+ return formatKey(from.hash, [network.scriptHash]);
653
+ throw new Error(`Unknown address type=${type}`);
654
+ },
655
+ decode(address) {
656
+ if (address.length < 14 || address.length > 74)
657
+ throw new Error('Invalid address length');
658
+ // Bech32
659
+ if (network.bech32 && address.toLowerCase().startsWith(network.bech32)) {
660
+ let res;
661
+ try {
662
+ res = base_1.bech32.decode(address);
663
+ if (res.words[0] !== 0)
664
+ throw new Error(`bech32: wrong version=${res.words[0]}`);
665
+ }
666
+ catch (_) {
667
+ // Starting from version 1 it is decoded as bech32m
668
+ res = base_1.bech32m.decode(address);
669
+ if (res.words[0] === 0)
670
+ throw new Error(`bech32m: wrong version=${res.words[0]}`);
671
+ }
672
+ if (res.prefix !== network.bech32)
673
+ throw new Error(`wrong bech32 prefix=${res.prefix}`);
674
+ const [version, ...program] = res.words;
675
+ const data = base_1.bech32.fromWords(program);
676
+ validateWitness(version, data);
677
+ if (version === 0 && data.length === 32)
678
+ return { type: 'wsh', hash: data };
679
+ else if (version === 0 && data.length === 20)
680
+ return { type: 'wpkh', hash: data };
681
+ else if (version === 1 && data.length === 32)
682
+ return { type: 'tr', pubkey: data };
683
+ else
684
+ throw new Error('Unknown witness program');
685
+ }
686
+ const data = base58check.decode(address);
687
+ if (data.length !== 21)
688
+ throw new Error('Invalid base58 address');
689
+ // Pay To Public Key Hash
690
+ if (data[0] === network.pubKeyHash) {
691
+ return { type: 'pkh', hash: data.slice(1) };
692
+ }
693
+ else if (data[0] === network.scriptHash) {
694
+ return {
695
+ type: 'sh',
696
+ hash: data.slice(1),
697
+ };
698
+ }
699
+ throw new Error(`Invalid address prefix=${data[0]}`);
700
+ },
701
+ };
702
+ }
703
+ exports.Address = Address;
704
+ //# sourceMappingURL=payment.js.map