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/LICENSE +21 -0
- package/README.md +220 -0
- package/dist/index.cjs +1391 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1020 -0
- package/dist/index.d.ts +1020 -0
- package/dist/index.js +1305 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
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
|