bip388 1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1391 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ NUMS: () => NUMS,
34
+ after: () => after,
35
+ and_v: () => and_v,
36
+ bareKey: () => bareKey,
37
+ bareMultiScript: () => bareMultiScript,
38
+ buildSegwitHtlcPolicy: () => buildSegwitHtlcPolicy,
39
+ buildSegwitMultisigPolicy: () => buildSegwitMultisigPolicy,
40
+ buildSegwitPolicy: () => buildSegwitPolicy,
41
+ buildTaprootHtlcPolicy: () => buildTaprootHtlcPolicy,
42
+ buildTaprootMultisigPolicy: () => buildTaprootMultisigPolicy,
43
+ buildTaprootPolicy: () => buildTaprootPolicy,
44
+ cltvScript: () => cltvScript,
45
+ csvScript: () => csvScript,
46
+ deriveFromXpub: () => deriveFromXpub,
47
+ encodeScriptInt: () => encodeScriptInt,
48
+ encodeScriptNumber: () => encodeScriptNumber,
49
+ extractKeyInfos: () => extractKeyInfos,
50
+ extractSegwitSecret: () => extractSecret2,
51
+ extractTaprootSecret: () => extractSecret,
52
+ formatKeyInfo: () => formatKeyInfo,
53
+ getNetwork: () => getNetwork,
54
+ hash160: () => hash160,
55
+ hash256: () => hash256,
56
+ hashlockScript: () => hashlockScript,
57
+ isRawKey: () => isRawKey,
58
+ isValidPoint: () => isValidPoint,
59
+ isXpubKey: () => isXpubKey,
60
+ keyPlaceholder: () => keyPlaceholder,
61
+ multi: () => multi,
62
+ multiScript: () => multiScript,
63
+ multi_a: () => multi_a,
64
+ older: () => older,
65
+ or_d: () => or_d,
66
+ or_i: () => or_i,
67
+ parsePublicKey: () => parsePublicKey,
68
+ pk: () => pk,
69
+ pkScript: () => pkScript,
70
+ pkh: () => pkh,
71
+ pkhScript: () => pkhScript,
72
+ pkhScriptFromHash: () => pkhScriptFromHash,
73
+ resolveCompressedPubkey: () => resolveCompressedPubkey,
74
+ resolveXOnlyPubkey: () => resolveXOnlyPubkey,
75
+ ripemd160: () => ripemd160,
76
+ segwitHtlc: () => segwitHtlc,
77
+ segwitMultisig: () => segwitMultisig,
78
+ sha256: () => sha256,
79
+ simpleCltvScript: () => simpleCltvScript,
80
+ simpleHashlockScript: () => simpleHashlockScript,
81
+ sortedBareMultiScript: () => sortedBareMultiScript,
82
+ sortedMultiScript: () => sortedMultiScript,
83
+ sortedmulti: () => sortedmulti,
84
+ sortedmulti_a: () => sortedmulti_a,
85
+ taprootHtlc: () => taprootHtlc,
86
+ taprootMultisig: () => taprootMultisig,
87
+ toKeyInfo: () => toKeyInfo,
88
+ toXOnly: () => toXOnly,
89
+ trDescriptor: () => trDescriptor,
90
+ v: () => v,
91
+ wpkhDescriptor: () => wpkhDescriptor,
92
+ wshDescriptor: () => wshDescriptor
93
+ });
94
+ module.exports = __toCommonJS(index_exports);
95
+
96
+ // src/miniscript.ts
97
+ function pk(key) {
98
+ return {
99
+ type: "pk",
100
+ key,
101
+ toString() {
102
+ return `pk(${this.key})`;
103
+ }
104
+ };
105
+ }
106
+ function pkh(key) {
107
+ return {
108
+ type: "pkh",
109
+ key,
110
+ toString() {
111
+ return `pkh(${this.key})`;
112
+ }
113
+ };
114
+ }
115
+ function sha256(hash) {
116
+ validateHash(hash, 64, "sha256");
117
+ return {
118
+ type: "sha256",
119
+ hash,
120
+ toString() {
121
+ return `sha256(${this.hash})`;
122
+ }
123
+ };
124
+ }
125
+ function hash256(hash) {
126
+ validateHash(hash, 64, "hash256");
127
+ return {
128
+ type: "hash256",
129
+ hash,
130
+ toString() {
131
+ return `hash256(${this.hash})`;
132
+ }
133
+ };
134
+ }
135
+ function hash160(hash) {
136
+ validateHash(hash, 40, "hash160");
137
+ return {
138
+ type: "hash160",
139
+ hash,
140
+ toString() {
141
+ return `hash160(${this.hash})`;
142
+ }
143
+ };
144
+ }
145
+ function ripemd160(hash) {
146
+ validateHash(hash, 40, "ripemd160");
147
+ return {
148
+ type: "ripemd160",
149
+ hash,
150
+ toString() {
151
+ return `ripemd160(${this.hash})`;
152
+ }
153
+ };
154
+ }
155
+ function older(blocks) {
156
+ validateTimelock(blocks, "older");
157
+ return {
158
+ type: "older",
159
+ blocks,
160
+ toString() {
161
+ return `older(${this.blocks})`;
162
+ }
163
+ };
164
+ }
165
+ function after(time) {
166
+ validateTimelock(time, "after");
167
+ return {
168
+ type: "after",
169
+ time,
170
+ toString() {
171
+ return `after(${this.time})`;
172
+ }
173
+ };
174
+ }
175
+ function and_v(left, right) {
176
+ return {
177
+ type: "and_v",
178
+ left,
179
+ right,
180
+ toString() {
181
+ return `and_v(${this.left.toString()},${this.right.toString()})`;
182
+ }
183
+ };
184
+ }
185
+ function or_d(left, right) {
186
+ return {
187
+ type: "or_d",
188
+ left,
189
+ right,
190
+ toString() {
191
+ return `or_d(${this.left.toString()},${this.right.toString()})`;
192
+ }
193
+ };
194
+ }
195
+ function or_i(left, right) {
196
+ return {
197
+ type: "or_i",
198
+ left,
199
+ right,
200
+ toString() {
201
+ return `or_i(${this.left.toString()},${this.right.toString()})`;
202
+ }
203
+ };
204
+ }
205
+ function v(inner) {
206
+ return {
207
+ type: "v",
208
+ inner,
209
+ toString() {
210
+ return `v:${this.inner.toString()}`;
211
+ }
212
+ };
213
+ }
214
+ function multi(threshold, keys) {
215
+ validateMultisig(threshold, keys.length, "multi");
216
+ return {
217
+ type: "multi",
218
+ threshold,
219
+ keys,
220
+ toString() {
221
+ return `multi(${this.threshold},${this.keys.join(",")})`;
222
+ }
223
+ };
224
+ }
225
+ function sortedmulti(threshold, keys) {
226
+ validateMultisig(threshold, keys.length, "sortedmulti");
227
+ return {
228
+ type: "sortedmulti",
229
+ threshold,
230
+ keys,
231
+ toString() {
232
+ return `sortedmulti(${this.threshold},${this.keys.join(",")})`;
233
+ }
234
+ };
235
+ }
236
+ function multi_a(threshold, keys) {
237
+ validateMultisig(threshold, keys.length, "multi_a");
238
+ return {
239
+ type: "multi_a",
240
+ threshold,
241
+ keys,
242
+ toString() {
243
+ return `multi_a(${this.threshold},${this.keys.join(",")})`;
244
+ }
245
+ };
246
+ }
247
+ function sortedmulti_a(threshold, keys) {
248
+ validateMultisig(threshold, keys.length, "sortedmulti_a");
249
+ return {
250
+ type: "sortedmulti_a",
251
+ threshold,
252
+ keys,
253
+ toString() {
254
+ return `sortedmulti_a(${this.threshold},${this.keys.join(",")})`;
255
+ }
256
+ };
257
+ }
258
+ function validateHash(hash, expectedLength, name) {
259
+ if (!hash || typeof hash !== "string") {
260
+ throw new Error(`${name} must be a hex string`);
261
+ }
262
+ if (hash.length !== expectedLength) {
263
+ throw new Error(
264
+ `${name} must be ${expectedLength / 2} bytes (${expectedLength} hex chars), got ${hash.length}`
265
+ );
266
+ }
267
+ if (!/^[0-9a-fA-F]+$/.test(hash)) {
268
+ throw new Error(`${name} must be a valid hex string`);
269
+ }
270
+ }
271
+ function validateTimelock(value, name) {
272
+ if (!Number.isInteger(value) || value <= 0) {
273
+ throw new Error(`${name} must be a positive integer`);
274
+ }
275
+ if (value > 2147483647) {
276
+ throw new Error(`${name} exceeds maximum value (2^31-1)`);
277
+ }
278
+ }
279
+ function validateMultisig(threshold, numKeys, name) {
280
+ if (!Number.isInteger(threshold) || threshold <= 0) {
281
+ throw new Error(`${name} threshold must be a positive integer`);
282
+ }
283
+ if (numKeys < 1) {
284
+ throw new Error(`${name} requires at least one key`);
285
+ }
286
+ if (threshold > numKeys) {
287
+ throw new Error(
288
+ `${name} threshold (${threshold}) cannot exceed number of keys (${numKeys})`
289
+ );
290
+ }
291
+ if (numKeys > 20 && (name === "multi" || name === "sortedmulti")) {
292
+ throw new Error(`${name} is limited to 20 keys in SegWit`);
293
+ }
294
+ }
295
+
296
+ // src/bip388.ts
297
+ function keyPlaceholder(index) {
298
+ if (!Number.isInteger(index) || index < 0) {
299
+ throw new Error(`Key index must be a non-negative integer, got: ${index}`);
300
+ }
301
+ return `@${index}/**`;
302
+ }
303
+ function trDescriptor(internalKeyIndex, tree) {
304
+ const internalKey = keyPlaceholder(internalKeyIndex);
305
+ if (!tree) {
306
+ return `tr(${internalKey})`;
307
+ }
308
+ const treeStr = serializeTapTree(tree);
309
+ return `tr(${internalKey},${treeStr})`;
310
+ }
311
+ function wshDescriptor(script) {
312
+ return `wsh(${script.toString()})`;
313
+ }
314
+ function wpkhDescriptor(keyIndex) {
315
+ return `wpkh(${keyPlaceholder(keyIndex)})`;
316
+ }
317
+ function buildTaprootPolicy(internalKeyIndex, tree, options) {
318
+ validatePolicyOptions(options);
319
+ return {
320
+ name: options.name,
321
+ template: trDescriptor(internalKeyIndex, tree),
322
+ keys: options.keys
323
+ };
324
+ }
325
+ function buildSegwitPolicy(script, options) {
326
+ validatePolicyOptions(options);
327
+ return {
328
+ name: options.name,
329
+ template: wshDescriptor(script),
330
+ keys: options.keys
331
+ };
332
+ }
333
+ function formatKeyInfo(info) {
334
+ let result = "";
335
+ if (info.fingerprint || info.originPath) {
336
+ result += "[";
337
+ if (info.fingerprint) {
338
+ result += info.fingerprint;
339
+ }
340
+ if (info.originPath) {
341
+ result += "/" + info.originPath;
342
+ }
343
+ result += "]";
344
+ }
345
+ result += info.xpub;
346
+ return result;
347
+ }
348
+ function bareKey(xpub) {
349
+ return { xpub };
350
+ }
351
+ function validatePolicyOptions(options) {
352
+ if (!options.name || typeof options.name !== "string") {
353
+ throw new Error("Policy name is required");
354
+ }
355
+ if (options.name.length > 64) {
356
+ throw new Error("Policy name should not exceed 64 characters");
357
+ }
358
+ if (!Array.isArray(options.keys) || options.keys.length === 0) {
359
+ throw new Error("At least one key is required");
360
+ }
361
+ }
362
+ function serializeTapTree(tree) {
363
+ if (Array.isArray(tree)) {
364
+ const [left, right] = tree;
365
+ return `{${serializeTapTree(left)},${serializeTapTree(right)}}`;
366
+ }
367
+ return tree.toString();
368
+ }
369
+
370
+ // src/patterns/htlc.ts
371
+ function buildTaprootHtlcPolicy(params) {
372
+ const { name, secretHash, timelock, numsKey, payeeKey, payerKey } = params;
373
+ validateHash2(secretHash, 64, "secretHash");
374
+ validateTimelock2(timelock, "timelock");
375
+ const secretLeaf = and_v(v(sha256(secretHash)), pk(keyPlaceholder(1)));
376
+ const timeoutLeaf = and_v(v(after(timelock)), pk(keyPlaceholder(2)));
377
+ const tree = [secretLeaf, timeoutLeaf];
378
+ return {
379
+ name,
380
+ template: trDescriptor(0, tree),
381
+ keys: [numsKey, payeeKey, payerKey]
382
+ };
383
+ }
384
+ function buildSegwitHtlcPolicy(params) {
385
+ const { name, secretHash, timelock, payeeKey, payerKey } = params;
386
+ validateHash2(secretHash, 64, "secretHash");
387
+ validateTimelock2(timelock, "timelock");
388
+ const secretPath = and_v(v(sha256(secretHash)), pk(keyPlaceholder(0)));
389
+ const timeoutPath = and_v(v(pk(keyPlaceholder(1))), after(timelock));
390
+ const script = or_i(secretPath, timeoutPath);
391
+ return {
392
+ name,
393
+ template: wshDescriptor(script),
394
+ keys: [payeeKey, payerKey]
395
+ };
396
+ }
397
+ function validateHash2(hash, expectedLength, name) {
398
+ if (!hash || typeof hash !== "string") {
399
+ throw new Error(`${name} must be a hex string`);
400
+ }
401
+ if (hash.length !== expectedLength) {
402
+ throw new Error(
403
+ `${name} must be ${expectedLength / 2} bytes (${expectedLength} hex chars), got ${hash.length}`
404
+ );
405
+ }
406
+ if (!/^[0-9a-fA-F]+$/.test(hash)) {
407
+ throw new Error(`${name} must be a valid hex string`);
408
+ }
409
+ }
410
+ function validateTimelock2(value, name) {
411
+ if (!Number.isInteger(value) || value <= 0) {
412
+ throw new Error(`${name} must be a positive integer`);
413
+ }
414
+ if (value > 2147483647) {
415
+ throw new Error(`${name} exceeds maximum value (2^31-1)`);
416
+ }
417
+ }
418
+
419
+ // src/patterns/multisig.ts
420
+ function buildSegwitMultisigPolicy(params) {
421
+ const { name, threshold, keys, sorted = true } = params;
422
+ if (keys.length === 0) {
423
+ throw new Error("At least one key is required");
424
+ }
425
+ const keyPlaceholders = keys.map((_, i) => keyPlaceholder(i));
426
+ const script = sorted ? sortedmulti(threshold, keyPlaceholders) : multi(threshold, keyPlaceholders);
427
+ return {
428
+ name,
429
+ template: wshDescriptor(script),
430
+ keys
431
+ };
432
+ }
433
+ function buildTaprootMultisigPolicy(params) {
434
+ const { name, threshold, keys, sorted = true } = params;
435
+ if (keys.length < 2) {
436
+ throw new Error(
437
+ "Taproot multisig requires at least 2 keys (internal key + script key)"
438
+ );
439
+ }
440
+ const scriptKeyPlaceholders = keys.slice(1).map((_, i) => keyPlaceholder(i + 1));
441
+ const script = sorted ? sortedmulti_a(threshold, scriptKeyPlaceholders) : multi_a(threshold, scriptKeyPlaceholders);
442
+ return {
443
+ name,
444
+ template: trDescriptor(0, script),
445
+ keys
446
+ };
447
+ }
448
+
449
+ // src/scripts/pk.ts
450
+ var import_bitcoinjs_lib = require("bitcoinjs-lib");
451
+ function pkScript(pubkey) {
452
+ if (pubkey.length !== 33 && pubkey.length !== 32) {
453
+ throw new Error(
454
+ `Invalid pubkey length: ${pubkey.length} (expected 32 or 33 bytes)`
455
+ );
456
+ }
457
+ return Buffer.from(import_bitcoinjs_lib.script.compile([pubkey, import_bitcoinjs_lib.opcodes.OP_CHECKSIG]));
458
+ }
459
+
460
+ // src/scripts/pkh.ts
461
+ var import_bitcoinjs_lib2 = require("bitcoinjs-lib");
462
+ function pkhScript(pubkey) {
463
+ if (pubkey.length !== 33) {
464
+ throw new Error(
465
+ `Invalid pubkey length: ${pubkey.length} (expected 33 bytes for compressed pubkey)`
466
+ );
467
+ }
468
+ const pubkeyHash = import_bitcoinjs_lib2.crypto.hash160(pubkey);
469
+ return Buffer.from(
470
+ import_bitcoinjs_lib2.script.compile([
471
+ import_bitcoinjs_lib2.opcodes.OP_DUP,
472
+ import_bitcoinjs_lib2.opcodes.OP_HASH160,
473
+ pubkeyHash,
474
+ import_bitcoinjs_lib2.opcodes.OP_EQUALVERIFY,
475
+ import_bitcoinjs_lib2.opcodes.OP_CHECKSIG
476
+ ])
477
+ );
478
+ }
479
+ function pkhScriptFromHash(pubkeyHash) {
480
+ if (pubkeyHash.length !== 20) {
481
+ throw new Error(
482
+ `Invalid pubkey hash length: ${pubkeyHash.length} (expected 20 bytes)`
483
+ );
484
+ }
485
+ return Buffer.from(
486
+ import_bitcoinjs_lib2.script.compile([
487
+ import_bitcoinjs_lib2.opcodes.OP_DUP,
488
+ import_bitcoinjs_lib2.opcodes.OP_HASH160,
489
+ pubkeyHash,
490
+ import_bitcoinjs_lib2.opcodes.OP_EQUALVERIFY,
491
+ import_bitcoinjs_lib2.opcodes.OP_CHECKSIG
492
+ ])
493
+ );
494
+ }
495
+
496
+ // src/scripts/multi.ts
497
+ var import_bitcoinjs_lib3 = require("bitcoinjs-lib");
498
+ function encodeScriptInt(n) {
499
+ if (n === 0) return import_bitcoinjs_lib3.opcodes.OP_0;
500
+ if (n >= 1 && n <= 16) return import_bitcoinjs_lib3.opcodes.OP_1 - 1 + n;
501
+ return Buffer.from(import_bitcoinjs_lib3.script.number.encode(n));
502
+ }
503
+ function multiScript(threshold, pubkeys, sorted = false) {
504
+ if (threshold <= 0 || !Number.isInteger(threshold)) {
505
+ throw new Error("threshold must be a positive integer");
506
+ }
507
+ if (pubkeys.length === 0) {
508
+ throw new Error("At least one pubkey is required");
509
+ }
510
+ if (threshold > pubkeys.length) {
511
+ throw new Error(
512
+ `threshold (${threshold}) cannot exceed number of pubkeys (${pubkeys.length})`
513
+ );
514
+ }
515
+ if (pubkeys.length > 20) {
516
+ throw new Error("SegWit multisig is limited to 20 keys");
517
+ }
518
+ for (const pk2 of pubkeys) {
519
+ if (pk2.length !== 33) {
520
+ throw new Error(
521
+ `Invalid pubkey length: ${pk2.length} (expected 33 bytes for SegWit)`
522
+ );
523
+ }
524
+ }
525
+ const keys = sorted ? [...pubkeys].sort(Buffer.compare) : pubkeys;
526
+ return Buffer.from(
527
+ import_bitcoinjs_lib3.script.compile([
528
+ encodeScriptInt(threshold),
529
+ ...keys,
530
+ encodeScriptInt(keys.length),
531
+ import_bitcoinjs_lib3.opcodes.OP_CHECKMULTISIG
532
+ ])
533
+ );
534
+ }
535
+ function sortedMultiScript(threshold, pubkeys) {
536
+ return multiScript(threshold, pubkeys, true);
537
+ }
538
+ function bareMultiScript(threshold, pubkeys, sorted = false) {
539
+ if (threshold <= 0 || !Number.isInteger(threshold)) {
540
+ throw new Error("threshold must be a positive integer");
541
+ }
542
+ if (pubkeys.length === 0) {
543
+ throw new Error("At least one pubkey is required");
544
+ }
545
+ if (threshold > pubkeys.length) {
546
+ throw new Error(
547
+ `threshold (${threshold}) cannot exceed number of pubkeys (${pubkeys.length})`
548
+ );
549
+ }
550
+ for (const pk2 of pubkeys) {
551
+ if (pk2.length !== 33 && pk2.length !== 65) {
552
+ throw new Error(
553
+ `Invalid pubkey length: ${pk2.length} (expected 33 or 65 bytes)`
554
+ );
555
+ }
556
+ }
557
+ const keys = sorted ? [...pubkeys].sort(Buffer.compare) : pubkeys;
558
+ return Buffer.from(
559
+ import_bitcoinjs_lib3.script.compile([
560
+ encodeScriptInt(threshold),
561
+ ...keys,
562
+ encodeScriptInt(keys.length),
563
+ import_bitcoinjs_lib3.opcodes.OP_CHECKMULTISIG
564
+ ])
565
+ );
566
+ }
567
+ function sortedBareMultiScript(threshold, pubkeys) {
568
+ return bareMultiScript(threshold, pubkeys, true);
569
+ }
570
+
571
+ // src/scripts/hashlock.ts
572
+ var import_bitcoinjs_lib4 = require("bitcoinjs-lib");
573
+ function getHashOpcode(hashType) {
574
+ switch (hashType) {
575
+ case "sha256":
576
+ return import_bitcoinjs_lib4.opcodes.OP_SHA256;
577
+ case "hash256":
578
+ return import_bitcoinjs_lib4.opcodes.OP_HASH256;
579
+ case "hash160":
580
+ return import_bitcoinjs_lib4.opcodes.OP_HASH160;
581
+ case "ripemd160":
582
+ return import_bitcoinjs_lib4.opcodes.OP_RIPEMD160;
583
+ }
584
+ }
585
+ function getHashLength(hashType) {
586
+ switch (hashType) {
587
+ case "sha256":
588
+ case "hash256":
589
+ return 32;
590
+ case "hash160":
591
+ case "ripemd160":
592
+ return 20;
593
+ }
594
+ }
595
+ function hashlockScript(hash, pubkey, hashType = "sha256") {
596
+ const expectedLen = getHashLength(hashType);
597
+ if (hash.length !== expectedLen) {
598
+ throw new Error(
599
+ `Invalid hash length for ${hashType}: ${hash.length} (expected ${expectedLen} bytes)`
600
+ );
601
+ }
602
+ if (pubkey.length !== 32 && pubkey.length !== 33) {
603
+ throw new Error(
604
+ `Invalid pubkey length: ${pubkey.length} (expected 32 or 33 bytes)`
605
+ );
606
+ }
607
+ return Buffer.from(
608
+ import_bitcoinjs_lib4.script.compile([
609
+ // v:sha256(H) - verify wrapper with SIZE check for preimage length
610
+ import_bitcoinjs_lib4.opcodes.OP_SIZE,
611
+ import_bitcoinjs_lib4.script.number.encode(expectedLen),
612
+ import_bitcoinjs_lib4.opcodes.OP_EQUALVERIFY,
613
+ // Hash check
614
+ getHashOpcode(hashType),
615
+ hash,
616
+ import_bitcoinjs_lib4.opcodes.OP_EQUALVERIFY,
617
+ // pk(K)
618
+ pubkey,
619
+ import_bitcoinjs_lib4.opcodes.OP_CHECKSIG
620
+ ])
621
+ );
622
+ }
623
+ function simpleHashlockScript(hash, hashType = "sha256") {
624
+ const expectedLen = getHashLength(hashType);
625
+ if (hash.length !== expectedLen) {
626
+ throw new Error(
627
+ `Invalid hash length for ${hashType}: ${hash.length} (expected ${expectedLen} bytes)`
628
+ );
629
+ }
630
+ return Buffer.from(
631
+ import_bitcoinjs_lib4.script.compile([
632
+ import_bitcoinjs_lib4.opcodes.OP_SIZE,
633
+ import_bitcoinjs_lib4.script.number.encode(expectedLen),
634
+ import_bitcoinjs_lib4.opcodes.OP_EQUALVERIFY,
635
+ getHashOpcode(hashType),
636
+ hash,
637
+ import_bitcoinjs_lib4.opcodes.OP_EQUAL
638
+ ])
639
+ );
640
+ }
641
+
642
+ // src/scripts/timelock.ts
643
+ var import_bitcoinjs_lib5 = require("bitcoinjs-lib");
644
+ function encodeScriptNumber(n) {
645
+ if (n === 0) {
646
+ return import_bitcoinjs_lib5.opcodes.OP_0;
647
+ }
648
+ if (n >= 1 && n <= 16) {
649
+ return import_bitcoinjs_lib5.opcodes.OP_1 - 1 + n;
650
+ }
651
+ return Buffer.from(import_bitcoinjs_lib5.script.number.encode(n));
652
+ }
653
+ function cltvScript(locktime, pubkey, useDrop = false) {
654
+ if (locktime <= 0) {
655
+ throw new Error("locktime must be a positive number");
656
+ }
657
+ if (locktime > 2147483647) {
658
+ throw new Error("locktime exceeds maximum value (2^31-1)");
659
+ }
660
+ if (pubkey.length !== 32 && pubkey.length !== 33) {
661
+ throw new Error(
662
+ `Invalid pubkey length: ${pubkey.length} (expected 32 or 33 bytes)`
663
+ );
664
+ }
665
+ return Buffer.from(
666
+ import_bitcoinjs_lib5.script.compile([
667
+ encodeScriptNumber(locktime),
668
+ import_bitcoinjs_lib5.opcodes.OP_CHECKLOCKTIMEVERIFY,
669
+ useDrop ? import_bitcoinjs_lib5.opcodes.OP_DROP : import_bitcoinjs_lib5.opcodes.OP_VERIFY,
670
+ pubkey,
671
+ import_bitcoinjs_lib5.opcodes.OP_CHECKSIG
672
+ ])
673
+ );
674
+ }
675
+ function csvScript(sequence, pubkey, useDrop = false) {
676
+ if (sequence <= 0) {
677
+ throw new Error("sequence must be a positive number");
678
+ }
679
+ if (sequence > 2147483647) {
680
+ throw new Error("sequence exceeds maximum value (2^31-1)");
681
+ }
682
+ if (pubkey.length !== 32 && pubkey.length !== 33) {
683
+ throw new Error(
684
+ `Invalid pubkey length: ${pubkey.length} (expected 32 or 33 bytes)`
685
+ );
686
+ }
687
+ return Buffer.from(
688
+ import_bitcoinjs_lib5.script.compile([
689
+ encodeScriptNumber(sequence),
690
+ import_bitcoinjs_lib5.opcodes.OP_CHECKSEQUENCEVERIFY,
691
+ useDrop ? import_bitcoinjs_lib5.opcodes.OP_DROP : import_bitcoinjs_lib5.opcodes.OP_VERIFY,
692
+ pubkey,
693
+ import_bitcoinjs_lib5.opcodes.OP_CHECKSIG
694
+ ])
695
+ );
696
+ }
697
+ function simpleCltvScript(locktime) {
698
+ if (locktime <= 0) {
699
+ throw new Error("locktime must be a positive number");
700
+ }
701
+ if (locktime > 2147483647) {
702
+ throw new Error("locktime exceeds maximum value (2^31-1)");
703
+ }
704
+ return Buffer.from(
705
+ import_bitcoinjs_lib5.script.compile([
706
+ encodeScriptNumber(locktime),
707
+ import_bitcoinjs_lib5.opcodes.OP_CHECKLOCKTIMEVERIFY,
708
+ import_bitcoinjs_lib5.opcodes.OP_DROP,
709
+ import_bitcoinjs_lib5.opcodes.OP_TRUE
710
+ ])
711
+ );
712
+ }
713
+
714
+ // src/keys.ts
715
+ var ecc = __toESM(require("@bitcoinerlab/secp256k1"), 1);
716
+ var import_bip32 = __toESM(require("bip32"), 1);
717
+ var import_bitcoinjs_lib6 = require("bitcoinjs-lib");
718
+
719
+ // src/types.ts
720
+ function isXpubKey(key) {
721
+ return "xpub" in key;
722
+ }
723
+ function isRawKey(key) {
724
+ return "pubkey" in key;
725
+ }
726
+
727
+ // src/keys.ts
728
+ var bip32 = (0, import_bip32.default)(ecc);
729
+ function getNetwork(name) {
730
+ switch (name) {
731
+ case "mainnet":
732
+ return import_bitcoinjs_lib6.networks.bitcoin;
733
+ case "testnet":
734
+ return import_bitcoinjs_lib6.networks.testnet;
735
+ case "regtest":
736
+ return import_bitcoinjs_lib6.networks.regtest;
737
+ }
738
+ }
739
+ function deriveFromXpub(xpub, change, index, network) {
740
+ const node = bip32.fromBase58(xpub, network);
741
+ return node.derive(change).derive(index);
742
+ }
743
+ function resolveCompressedPubkey(key, network) {
744
+ if (isXpubKey(key)) {
745
+ const net = getNetwork(network);
746
+ const child = deriveFromXpub(
747
+ key.xpub,
748
+ key.change ?? 0,
749
+ key.index ?? 0,
750
+ net
751
+ );
752
+ return Buffer.from(child.publicKey);
753
+ } else {
754
+ return parsePublicKey(key.pubkey);
755
+ }
756
+ }
757
+ function resolveXOnlyPubkey(key, network) {
758
+ const compressed = resolveCompressedPubkey(key, network);
759
+ return toXOnly(compressed);
760
+ }
761
+ function toXOnly(pubkey) {
762
+ if (pubkey.length === 32) {
763
+ return pubkey;
764
+ }
765
+ if (pubkey.length === 33) {
766
+ return pubkey.subarray(1, 33);
767
+ }
768
+ if (pubkey.length === 65) {
769
+ return pubkey.subarray(1, 33);
770
+ }
771
+ throw new Error(`Invalid public key length: ${pubkey.length}`);
772
+ }
773
+ function parsePublicKey(pubkeyHex) {
774
+ const buf = Buffer.from(pubkeyHex, "hex");
775
+ if (buf.length === 33) {
776
+ if (buf[0] !== 2 && buf[0] !== 3) {
777
+ throw new Error("Compressed public key must start with 02 or 03");
778
+ }
779
+ if (!isValidPoint(buf)) {
780
+ throw new Error("Invalid public key: not a valid curve point");
781
+ }
782
+ return buf;
783
+ }
784
+ if (buf.length === 32) {
785
+ const compressed = Buffer.concat([Buffer.from([2]), buf]);
786
+ if (!isValidPoint(compressed)) {
787
+ throw new Error("Invalid x-only public key: not a valid curve point");
788
+ }
789
+ return compressed;
790
+ }
791
+ if (buf.length === 65) {
792
+ if (buf[0] !== 4) {
793
+ throw new Error("Uncompressed public key must start with 04");
794
+ }
795
+ const prefix = (buf[64] & 1) === 0 ? 2 : 3;
796
+ const compressed = Buffer.concat([
797
+ Buffer.from([prefix]),
798
+ buf.subarray(1, 33)
799
+ ]);
800
+ if (!isValidPoint(compressed)) {
801
+ throw new Error("Invalid public key: not a valid curve point");
802
+ }
803
+ return compressed;
804
+ }
805
+ throw new Error(
806
+ `Invalid public key length: ${buf.length} bytes (expected 32, 33, or 65)`
807
+ );
808
+ }
809
+ function isValidPoint(pubkey) {
810
+ try {
811
+ return ecc.isPoint(pubkey);
812
+ } catch {
813
+ return false;
814
+ }
815
+ }
816
+ function toKeyInfo(key) {
817
+ return {
818
+ fingerprint: key.fingerprint,
819
+ originPath: key.originPath,
820
+ xpub: key.xpub
821
+ };
822
+ }
823
+ function extractKeyInfos(keys) {
824
+ const infos = [];
825
+ for (const key of keys) {
826
+ if (!isXpubKey(key)) {
827
+ return null;
828
+ }
829
+ infos.push(toKeyInfo(key));
830
+ }
831
+ return infos;
832
+ }
833
+
834
+ // src/patterns/taproot/htlc.ts
835
+ var ecc3 = __toESM(require("@bitcoinerlab/secp256k1"), 1);
836
+ var import_bitcoinjs_lib8 = require("bitcoinjs-lib");
837
+ var import_sha2 = require("@noble/hashes/sha2.js");
838
+
839
+ // src/patterns/taproot/nums.ts
840
+ var NUMS = {
841
+ /** Compressed public key (33 bytes as hex) */
842
+ pubkey: "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
843
+ /** X-only public key (32 bytes as hex) - for taproot internal key */
844
+ xonly: "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
845
+ /** X-only as Buffer */
846
+ get xonlyBuffer() {
847
+ return Buffer.from(this.xonly, "hex");
848
+ },
849
+ /** Compressed as Buffer */
850
+ get pubkeyBuffer() {
851
+ return Buffer.from(this.pubkey, "hex");
852
+ },
853
+ /**
854
+ * Get NUMS xpub for a specific network.
855
+ * Constructed with:
856
+ * - Public key: NUMS point
857
+ * - Chain code: 32 zero bytes
858
+ * - Depth/parent/index: all zeros (acts like master key)
859
+ */
860
+ xpub(network) {
861
+ switch (network) {
862
+ case "mainnet":
863
+ return "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57";
864
+ case "testnet":
865
+ case "regtest":
866
+ return "tpubD6NzVbkrYhZ4WLczPJWReQycCJdd6YVWXubbVUFnJ5KgU5MDQrD998ZJLSmaB7GVcCnJSDWprxmrGkJ6SvgQC6QAffVpqSvonXmeizXcrkN";
867
+ }
868
+ }
869
+ };
870
+
871
+ // src/patterns/taproot/script.ts
872
+ var ecc2 = __toESM(require("@bitcoinerlab/secp256k1"), 1);
873
+ var import_bitcoinjs_lib7 = require("bitcoinjs-lib");
874
+ var LEAF_VERSION = 192;
875
+ function tapLeafHash(script) {
876
+ return Buffer.from(
877
+ import_bitcoinjs_lib7.crypto.taggedHash(
878
+ "TapLeaf",
879
+ Buffer.concat([Buffer.from([LEAF_VERSION]), serializeScript(script)])
880
+ )
881
+ );
882
+ }
883
+ function tapBranchHash(left, right) {
884
+ const [first, second] = Buffer.compare(left, right) <= 0 ? [left, right] : [right, left];
885
+ return Buffer.from(
886
+ import_bitcoinjs_lib7.crypto.taggedHash("TapBranch", Buffer.concat([first, second]))
887
+ );
888
+ }
889
+ function tapTweak(internalPubkey, merkleRoot) {
890
+ return Buffer.from(
891
+ import_bitcoinjs_lib7.crypto.taggedHash("TapTweak", Buffer.concat([internalPubkey, merkleRoot]))
892
+ );
893
+ }
894
+ function serializeScript(script) {
895
+ const len = varintEncode(script.length);
896
+ return Buffer.concat([len, script]);
897
+ }
898
+ function buildControlBlock(internalPubkey, leafVersion, merklePath) {
899
+ const parityByte = leafVersion;
900
+ return Buffer.concat([
901
+ Buffer.from([parityByte]),
902
+ internalPubkey,
903
+ ...merklePath
904
+ ]);
905
+ }
906
+ function varintEncode(n) {
907
+ if (n < 253) {
908
+ const buf = Buffer.allocUnsafe(1);
909
+ buf.writeUInt8(n, 0);
910
+ return buf;
911
+ } else if (n <= 65535) {
912
+ const buf = Buffer.allocUnsafe(3);
913
+ buf.writeUInt8(253, 0);
914
+ buf.writeUInt16LE(n, 1);
915
+ return buf;
916
+ } else if (n <= 4294967295) {
917
+ const buf = Buffer.allocUnsafe(5);
918
+ buf.writeUInt8(254, 0);
919
+ buf.writeUInt32LE(n, 1);
920
+ return buf;
921
+ } else {
922
+ const buf = Buffer.allocUnsafe(9);
923
+ buf.writeUInt8(255, 0);
924
+ buf.writeUInt32LE(n >>> 0, 1);
925
+ buf.writeUInt32LE(n / 4294967296 | 0, 5);
926
+ return buf;
927
+ }
928
+ }
929
+ function encodeScriptNumber2(n) {
930
+ return Buffer.from(import_bitcoinjs_lib7.script.number.encode(n));
931
+ }
932
+ function buildP2TROutput(outputKey) {
933
+ return Buffer.concat([
934
+ Buffer.from([81, 32]),
935
+ // OP_1, push 32 bytes
936
+ outputKey
937
+ ]);
938
+ }
939
+
940
+ // src/patterns/taproot/htlc.ts
941
+ (0, import_bitcoinjs_lib8.initEccLib)(ecc3);
942
+ function taprootHtlc(params) {
943
+ const { payee, payer, secretHash, timelock, network, name } = params;
944
+ if (!secretHash || secretHash.length !== 64) {
945
+ throw new Error("secretHash must be 32 bytes (64 hex chars)");
946
+ }
947
+ if (timelock <= 0) {
948
+ throw new Error("timelock must be a positive number");
949
+ }
950
+ const payeeXOnly = resolveXOnlyPubkey(payee, network);
951
+ const payerXOnly = resolveXOnlyPubkey(payer, network);
952
+ const hashBuffer = Buffer.from(secretHash, "hex");
953
+ const secretScript = buildHashLockScript(hashBuffer, payeeXOnly);
954
+ const timeoutScript = buildTimelockScript(timelock, payerXOnly);
955
+ const secretLeafHash = tapLeafHash(secretScript);
956
+ const timeoutLeafHash = tapLeafHash(timeoutScript);
957
+ const merkleRoot = tapBranchHash(secretLeafHash, timeoutLeafHash);
958
+ const net = getNetwork(network);
959
+ const numsXpub = NUMS.xpub(network);
960
+ const derivedNums = deriveFromXpub(numsXpub, 0, 0, net);
961
+ const internalPubkey = toXOnly(Buffer.from(derivedNums.publicKey));
962
+ const tweak = tapTweak(internalPubkey, merkleRoot);
963
+ const tweakedKey = ecc3.xOnlyPointAddTweak(internalPubkey, tweak);
964
+ if (!tweakedKey) {
965
+ throw new Error("Failed to tweak internal key");
966
+ }
967
+ const outputKey = Buffer.from(tweakedKey.xOnlyPubkey);
968
+ const scriptPubkey = buildP2TROutput(outputKey);
969
+ const address = import_bitcoinjs_lib8.payments.p2tr({
970
+ pubkey: outputKey,
971
+ network: net
972
+ }).address;
973
+ const secretControlBlock = buildControlBlock(
974
+ internalPubkey,
975
+ 192 | (tweakedKey.parity ? 1 : 0),
976
+ [timeoutLeafHash]
977
+ // path to sibling
978
+ );
979
+ const timeoutControlBlock = buildControlBlock(
980
+ internalPubkey,
981
+ 192 | (tweakedKey.parity ? 1 : 0),
982
+ [secretLeafHash]
983
+ // path to sibling
984
+ );
985
+ let policy = null;
986
+ if (isXpubKey(payee) && isXpubKey(payer)) {
987
+ policy = buildTaprootHtlcPolicy({
988
+ name: name || "HTLC",
989
+ secretHash,
990
+ timelock,
991
+ numsKey: bareKey(NUMS.xpub(network)),
992
+ payeeKey: toKeyInfo(payee),
993
+ payerKey: toKeyInfo(payer)
994
+ });
995
+ }
996
+ return {
997
+ address,
998
+ scriptPubkey,
999
+ internalPubkey,
1000
+ merkleRoot,
1001
+ tweak,
1002
+ outputKey,
1003
+ timelock,
1004
+ secretLeaf: {
1005
+ script: secretScript,
1006
+ hash: secretLeafHash
1007
+ },
1008
+ timeoutLeaf: {
1009
+ script: timeoutScript,
1010
+ hash: timeoutLeafHash
1011
+ },
1012
+ controlBlock(leaf) {
1013
+ return leaf === "secret" ? secretControlBlock : timeoutControlBlock;
1014
+ },
1015
+ witness: {
1016
+ secret(signature, preimage) {
1017
+ return [signature, preimage, secretScript, secretControlBlock];
1018
+ },
1019
+ timeout(signature) {
1020
+ return [signature, timeoutScript, timeoutControlBlock];
1021
+ }
1022
+ },
1023
+ policy
1024
+ };
1025
+ }
1026
+ function extractSecret(witness, secretHash) {
1027
+ if (!witness || witness.length < 4) {
1028
+ return null;
1029
+ }
1030
+ const preimage = witness[1];
1031
+ if (preimage.length !== 32) {
1032
+ return null;
1033
+ }
1034
+ if (secretHash) {
1035
+ const computed = Buffer.from((0, import_sha2.sha256)(preimage)).toString("hex");
1036
+ if (computed !== secretHash) {
1037
+ return null;
1038
+ }
1039
+ }
1040
+ return preimage.toString("hex");
1041
+ }
1042
+ function buildHashLockScript(hash, pubkey, hashOp = import_bitcoinjs_lib8.opcodes.OP_SHA256) {
1043
+ return Buffer.from(
1044
+ import_bitcoinjs_lib8.script.compile([
1045
+ // v:sha256(H) - verify wrapper adds SIZE check for 32-byte preimage
1046
+ import_bitcoinjs_lib8.opcodes.OP_SIZE,
1047
+ import_bitcoinjs_lib8.script.number.encode(32),
1048
+ // Push number 32
1049
+ import_bitcoinjs_lib8.opcodes.OP_EQUALVERIFY,
1050
+ // sha256(H)
1051
+ hashOp,
1052
+ hash,
1053
+ import_bitcoinjs_lib8.opcodes.OP_EQUALVERIFY,
1054
+ // pk(K)
1055
+ pubkey,
1056
+ import_bitcoinjs_lib8.opcodes.OP_CHECKSIG
1057
+ ])
1058
+ );
1059
+ }
1060
+ function buildTimelockScript(timelock, pubkey) {
1061
+ return Buffer.from(
1062
+ import_bitcoinjs_lib8.script.compile([
1063
+ encodeScriptNumber2(timelock),
1064
+ import_bitcoinjs_lib8.opcodes.OP_CHECKLOCKTIMEVERIFY,
1065
+ import_bitcoinjs_lib8.opcodes.OP_VERIFY,
1066
+ pubkey,
1067
+ import_bitcoinjs_lib8.opcodes.OP_CHECKSIG
1068
+ ])
1069
+ );
1070
+ }
1071
+
1072
+ // src/patterns/taproot/multisig.ts
1073
+ var ecc4 = __toESM(require("@bitcoinerlab/secp256k1"), 1);
1074
+ var import_bitcoinjs_lib9 = require("bitcoinjs-lib");
1075
+ (0, import_bitcoinjs_lib9.initEccLib)(ecc4);
1076
+ function taprootMultisig(params) {
1077
+ const { threshold, keys, sorted = true, network, name } = params;
1078
+ if (threshold <= 0 || !Number.isInteger(threshold)) {
1079
+ throw new Error("threshold must be a positive integer");
1080
+ }
1081
+ if (keys.length === 0) {
1082
+ throw new Error("At least one key is required");
1083
+ }
1084
+ if (threshold > keys.length) {
1085
+ throw new Error(
1086
+ `threshold (${threshold}) cannot exceed number of keys (${keys.length})`
1087
+ );
1088
+ }
1089
+ const net = getNetwork(network);
1090
+ let pubkeys = keys.map((key) => resolveXOnlyPubkey(key, network));
1091
+ let sortedIndexes = pubkeys.map((_, i) => i);
1092
+ if (sorted) {
1093
+ const indexed = pubkeys.map((pk2, i) => ({ pk: pk2, i }));
1094
+ indexed.sort(
1095
+ (a, b) => Buffer.compare(a.pk, b.pk)
1096
+ );
1097
+ pubkeys = indexed.map((x) => x.pk);
1098
+ sortedIndexes = indexed.map((x) => x.i);
1099
+ }
1100
+ const scriptParts = [];
1101
+ scriptParts.push(pubkeys[0]);
1102
+ scriptParts.push(import_bitcoinjs_lib9.opcodes.OP_CHECKSIG);
1103
+ for (let i = 1; i < pubkeys.length; i++) {
1104
+ scriptParts.push(pubkeys[i]);
1105
+ scriptParts.push(import_bitcoinjs_lib9.opcodes.OP_CHECKSIGADD);
1106
+ }
1107
+ scriptParts.push(encodeScriptNumber(threshold));
1108
+ scriptParts.push(import_bitcoinjs_lib9.opcodes.OP_NUMEQUAL);
1109
+ const multisigScript = Buffer.from(import_bitcoinjs_lib9.script.compile(scriptParts));
1110
+ const leafHash = tapLeafHash(multisigScript);
1111
+ const internalPubkey = NUMS.xonlyBuffer;
1112
+ const merkleRoot = leafHash;
1113
+ const tweak = tapTweak(internalPubkey, merkleRoot);
1114
+ const tweakedKey = ecc4.xOnlyPointAddTweak(internalPubkey, tweak);
1115
+ if (!tweakedKey) {
1116
+ throw new Error("Failed to tweak internal key");
1117
+ }
1118
+ const outputKey = Buffer.from(tweakedKey.xOnlyPubkey);
1119
+ const scriptPubkey = buildP2TROutput(outputKey);
1120
+ const address = import_bitcoinjs_lib9.payments.p2tr({
1121
+ pubkey: outputKey,
1122
+ network: net
1123
+ }).address;
1124
+ const controlBlock = buildControlBlock(
1125
+ internalPubkey,
1126
+ 192 | (tweakedKey.parity ? 1 : 0),
1127
+ []
1128
+ // No sibling for single-leaf tree
1129
+ );
1130
+ let policy = null;
1131
+ const keyInfos = extractKeyInfos(keys);
1132
+ if (keyInfos) {
1133
+ policy = buildTaprootMultisigPolicy({
1134
+ name: name || `${threshold}-of-${keys.length} Taproot Multisig`,
1135
+ threshold,
1136
+ keys: [bareKey(NUMS.xpub(network)), ...keyInfos],
1137
+ sorted
1138
+ });
1139
+ }
1140
+ return {
1141
+ address,
1142
+ scriptPubkey,
1143
+ internalPubkey,
1144
+ merkleRoot,
1145
+ tweak,
1146
+ outputKey,
1147
+ scriptLeaf: {
1148
+ script: multisigScript,
1149
+ hash: leafHash
1150
+ },
1151
+ controlBlock,
1152
+ policy,
1153
+ /**
1154
+ * Build witness stack for script path spend.
1155
+ *
1156
+ * For multi_a, signatures must be provided in the same order as keys appear
1157
+ * in the script. Use empty Buffer for keys that don't sign.
1158
+ *
1159
+ * @param signatures Array of signatures (length must equal number of keys)
1160
+ * Use Buffer.alloc(0) for non-signing keys
1161
+ * @returns Witness stack: [sig1, sig2, ..., sigN, script, control_block]
1162
+ */
1163
+ witness(signatures) {
1164
+ if (signatures.length !== keys.length) {
1165
+ throw new Error(
1166
+ `Expected ${keys.length} signature slots (use empty Buffer for non-signers), got ${signatures.length}`
1167
+ );
1168
+ }
1169
+ const sigCount = signatures.filter((s) => s.length > 0).length;
1170
+ if (sigCount !== threshold) {
1171
+ throw new Error(`Expected ${threshold} signatures, got ${sigCount}`);
1172
+ }
1173
+ const orderedSigs = sorted ? sortedIndexes.map((origIdx) => signatures[origIdx]) : signatures;
1174
+ return [...orderedSigs.reverse(), multisigScript, controlBlock];
1175
+ }
1176
+ };
1177
+ }
1178
+
1179
+ // src/patterns/segwit/htlc.ts
1180
+ var import_bitcoinjs_lib10 = require("bitcoinjs-lib");
1181
+ var import_sha22 = require("@noble/hashes/sha2.js");
1182
+ function segwitHtlc(params) {
1183
+ const { payee, payer, secretHash, timelock, network, name } = params;
1184
+ if (!secretHash || secretHash.length !== 64) {
1185
+ throw new Error("secretHash must be 32 bytes (64 hex chars)");
1186
+ }
1187
+ if (timelock <= 0) {
1188
+ throw new Error("timelock must be a positive number");
1189
+ }
1190
+ const payeePubkey = resolveCompressedPubkey(payee, network);
1191
+ const payerPubkey = resolveCompressedPubkey(payer, network);
1192
+ const secretHashBuffer = Buffer.from(secretHash, "hex");
1193
+ const witnessScript = Buffer.from(
1194
+ import_bitcoinjs_lib10.script.compile([
1195
+ import_bitcoinjs_lib10.opcodes.OP_IF,
1196
+ // and_v(v:sha256(H), pk(@0)) - secret path
1197
+ import_bitcoinjs_lib10.opcodes.OP_SIZE,
1198
+ import_bitcoinjs_lib10.script.number.encode(32),
1199
+ import_bitcoinjs_lib10.opcodes.OP_EQUALVERIFY,
1200
+ import_bitcoinjs_lib10.opcodes.OP_SHA256,
1201
+ secretHashBuffer,
1202
+ import_bitcoinjs_lib10.opcodes.OP_EQUALVERIFY,
1203
+ payeePubkey,
1204
+ import_bitcoinjs_lib10.opcodes.OP_CHECKSIG,
1205
+ import_bitcoinjs_lib10.opcodes.OP_ELSE,
1206
+ // and_v(v:pk(@1), after(N)) - timeout path
1207
+ payerPubkey,
1208
+ import_bitcoinjs_lib10.opcodes.OP_CHECKSIGVERIFY,
1209
+ import_bitcoinjs_lib10.script.number.encode(timelock),
1210
+ import_bitcoinjs_lib10.opcodes.OP_CHECKLOCKTIMEVERIFY,
1211
+ import_bitcoinjs_lib10.opcodes.OP_ENDIF
1212
+ ])
1213
+ );
1214
+ const net = getNetwork(network);
1215
+ const scriptHash = (0, import_sha22.sha256)(witnessScript);
1216
+ const p2wsh = import_bitcoinjs_lib10.payments.p2wsh({
1217
+ redeem: { output: witnessScript, network: net },
1218
+ network: net
1219
+ });
1220
+ if (!p2wsh.address || !p2wsh.output) {
1221
+ throw new Error("Failed to create P2WSH output");
1222
+ }
1223
+ let policy = null;
1224
+ if (isXpubKey(payee) && isXpubKey(payer)) {
1225
+ policy = buildSegwitHtlcPolicy({
1226
+ name: name || "HTLC",
1227
+ secretHash,
1228
+ timelock,
1229
+ payeeKey: toKeyInfo(payee),
1230
+ payerKey: toKeyInfo(payer)
1231
+ });
1232
+ }
1233
+ return {
1234
+ address: p2wsh.address,
1235
+ scriptPubkey: Buffer.from(p2wsh.output),
1236
+ witnessScript,
1237
+ scriptHash: Buffer.from(scriptHash),
1238
+ timelock,
1239
+ witness: {
1240
+ secret(signature, preimage) {
1241
+ return [signature, preimage, Buffer.from([1]), witnessScript];
1242
+ },
1243
+ timeout(signature) {
1244
+ return [signature, Buffer.alloc(0), witnessScript];
1245
+ }
1246
+ },
1247
+ policy
1248
+ };
1249
+ }
1250
+ function extractSecret2(witness, secretHash) {
1251
+ if (!witness || witness.length < 4) {
1252
+ return null;
1253
+ }
1254
+ const preimage = witness[1];
1255
+ if (preimage.length !== 32) {
1256
+ return null;
1257
+ }
1258
+ if (secretHash) {
1259
+ const computed = Buffer.from((0, import_sha22.sha256)(preimage)).toString("hex");
1260
+ if (computed !== secretHash) {
1261
+ return null;
1262
+ }
1263
+ }
1264
+ return preimage.toString("hex");
1265
+ }
1266
+
1267
+ // src/patterns/segwit/multisig.ts
1268
+ var import_bitcoinjs_lib11 = require("bitcoinjs-lib");
1269
+ var import_sha23 = require("@noble/hashes/sha2.js");
1270
+ function segwitMultisig(params) {
1271
+ const { threshold, keys, sorted = true, network, name } = params;
1272
+ if (threshold <= 0 || !Number.isInteger(threshold)) {
1273
+ throw new Error("threshold must be a positive integer");
1274
+ }
1275
+ if (keys.length === 0) {
1276
+ throw new Error("At least one key is required");
1277
+ }
1278
+ if (threshold > keys.length) {
1279
+ throw new Error(
1280
+ `threshold (${threshold}) cannot exceed number of keys (${keys.length})`
1281
+ );
1282
+ }
1283
+ if (keys.length > 20) {
1284
+ throw new Error("SegWit multisig is limited to 20 keys");
1285
+ }
1286
+ const net = getNetwork(network);
1287
+ const pubkeys = keys.map((key) => resolveCompressedPubkey(key, network));
1288
+ const witnessScript = multiScript(threshold, pubkeys, sorted);
1289
+ const scriptHash = (0, import_sha23.sha256)(witnessScript);
1290
+ const p2wsh = import_bitcoinjs_lib11.payments.p2wsh({
1291
+ redeem: { output: witnessScript, network: net },
1292
+ network: net
1293
+ });
1294
+ if (!p2wsh.address || !p2wsh.output) {
1295
+ throw new Error("Failed to create P2WSH output");
1296
+ }
1297
+ let policy = null;
1298
+ const keyInfos = extractKeyInfos(keys);
1299
+ if (keyInfos) {
1300
+ policy = buildSegwitMultisigPolicy({
1301
+ name: name || `${threshold}-of-${keys.length} Multisig`,
1302
+ threshold,
1303
+ keys: keyInfos,
1304
+ sorted
1305
+ });
1306
+ }
1307
+ return {
1308
+ address: p2wsh.address,
1309
+ scriptPubkey: Buffer.from(p2wsh.output),
1310
+ witnessScript,
1311
+ scriptHash: Buffer.from(scriptHash),
1312
+ policy,
1313
+ /**
1314
+ * Build witness stack for spending.
1315
+ * @param signatures Array of signatures (must have exactly `threshold` signatures)
1316
+ * @returns Witness stack: [OP_0, sig1, sig2, ..., witnessScript]
1317
+ */
1318
+ witness(signatures) {
1319
+ if (signatures.length !== threshold) {
1320
+ throw new Error(
1321
+ `Expected ${threshold} signatures, got ${signatures.length}`
1322
+ );
1323
+ }
1324
+ return [Buffer.alloc(0), ...signatures, witnessScript];
1325
+ }
1326
+ };
1327
+ }
1328
+ // Annotate the CommonJS export names for ESM import in node:
1329
+ 0 && (module.exports = {
1330
+ NUMS,
1331
+ after,
1332
+ and_v,
1333
+ bareKey,
1334
+ bareMultiScript,
1335
+ buildSegwitHtlcPolicy,
1336
+ buildSegwitMultisigPolicy,
1337
+ buildSegwitPolicy,
1338
+ buildTaprootHtlcPolicy,
1339
+ buildTaprootMultisigPolicy,
1340
+ buildTaprootPolicy,
1341
+ cltvScript,
1342
+ csvScript,
1343
+ deriveFromXpub,
1344
+ encodeScriptInt,
1345
+ encodeScriptNumber,
1346
+ extractKeyInfos,
1347
+ extractSegwitSecret,
1348
+ extractTaprootSecret,
1349
+ formatKeyInfo,
1350
+ getNetwork,
1351
+ hash160,
1352
+ hash256,
1353
+ hashlockScript,
1354
+ isRawKey,
1355
+ isValidPoint,
1356
+ isXpubKey,
1357
+ keyPlaceholder,
1358
+ multi,
1359
+ multiScript,
1360
+ multi_a,
1361
+ older,
1362
+ or_d,
1363
+ or_i,
1364
+ parsePublicKey,
1365
+ pk,
1366
+ pkScript,
1367
+ pkh,
1368
+ pkhScript,
1369
+ pkhScriptFromHash,
1370
+ resolveCompressedPubkey,
1371
+ resolveXOnlyPubkey,
1372
+ ripemd160,
1373
+ segwitHtlc,
1374
+ segwitMultisig,
1375
+ sha256,
1376
+ simpleCltvScript,
1377
+ simpleHashlockScript,
1378
+ sortedBareMultiScript,
1379
+ sortedMultiScript,
1380
+ sortedmulti,
1381
+ sortedmulti_a,
1382
+ taprootHtlc,
1383
+ taprootMultisig,
1384
+ toKeyInfo,
1385
+ toXOnly,
1386
+ trDescriptor,
1387
+ v,
1388
+ wpkhDescriptor,
1389
+ wshDescriptor
1390
+ });
1391
+ //# sourceMappingURL=index.cjs.map