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