utxo-descriptors 0.1.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 +128 -0
- package/dist/index.cjs +1287 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +54 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +1269 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.cjs +725 -0
- package/dist/parse.cjs.map +1 -0
- package/dist/parse.d.cts +10 -0
- package/dist/parse.d.ts +10 -0
- package/dist/parse.js +723 -0
- package/dist/parse.js.map +1 -0
- package/dist/types-Dr-JC_LD.d.cts +107 -0
- package/dist/types-Dr-JC_LD.d.ts +107 -0
- package/dist/weights-9_pPEdMA.d.cts +68 -0
- package/dist/weights-B0KrRWLe.d.ts +68 -0
- package/dist/weights.cjs +490 -0
- package/dist/weights.cjs.map +1 -0
- package/dist/weights.d.cts +2 -0
- package/dist/weights.d.ts +2 -0
- package/dist/weights.js +483 -0
- package/dist/weights.js.map +1 -0
- package/package.json +67 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1287 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
|
|
7
|
+
// src/chains.ts
|
|
8
|
+
var BITCOIN = {
|
|
9
|
+
name: "bitcoin",
|
|
10
|
+
bech32Hrp: "bc",
|
|
11
|
+
base58PubKeyHash: 0,
|
|
12
|
+
base58ScriptHash: 5
|
|
13
|
+
};
|
|
14
|
+
var BITCOIN_TESTNET = {
|
|
15
|
+
name: "bitcoin-testnet",
|
|
16
|
+
bech32Hrp: "tb",
|
|
17
|
+
base58PubKeyHash: 111,
|
|
18
|
+
base58ScriptHash: 196
|
|
19
|
+
};
|
|
20
|
+
var LITECOIN = {
|
|
21
|
+
name: "litecoin",
|
|
22
|
+
bech32Hrp: "ltc",
|
|
23
|
+
base58PubKeyHash: 48,
|
|
24
|
+
base58ScriptHash: 50
|
|
25
|
+
};
|
|
26
|
+
var LITECOIN_TESTNET = {
|
|
27
|
+
name: "litecoin-testnet",
|
|
28
|
+
bech32Hrp: "tltc",
|
|
29
|
+
base58PubKeyHash: 111,
|
|
30
|
+
base58ScriptHash: 58
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/checksum.ts
|
|
34
|
+
var INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
|
|
35
|
+
var CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
36
|
+
var GENERATOR = [0xf5dee51989n, 0xa9fdca3312n, 0x1bab10e32dn, 0x3706b1677an, 0x644d626ffdn];
|
|
37
|
+
var CHECKSUM_LENGTH = 8;
|
|
38
|
+
var GROUP_SIZE = 3;
|
|
39
|
+
var GROUP_BITS = 5;
|
|
40
|
+
var GROUP_MASK = 0x7ffffffffn;
|
|
41
|
+
var TOP_SHIFT = 35n;
|
|
42
|
+
var SYMBOL_MASK = 31;
|
|
43
|
+
function polymod(symbols) {
|
|
44
|
+
let checksum = 1n;
|
|
45
|
+
for (const symbol of symbols) {
|
|
46
|
+
const top = checksum >> TOP_SHIFT;
|
|
47
|
+
checksum = (checksum & GROUP_MASK) << 5n ^ BigInt(symbol);
|
|
48
|
+
for (let bit = 0; bit < GENERATOR.length; bit += 1) {
|
|
49
|
+
const generatorValue = GENERATOR[bit];
|
|
50
|
+
if (generatorValue !== void 0 && (top >> BigInt(bit) & 1n) === 1n) {
|
|
51
|
+
checksum ^= generatorValue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return checksum;
|
|
56
|
+
}
|
|
57
|
+
function expand(text) {
|
|
58
|
+
const symbols = [];
|
|
59
|
+
let groupAccumulator = 0;
|
|
60
|
+
let groupCount = 0;
|
|
61
|
+
for (const char of text) {
|
|
62
|
+
const value = INPUT_CHARSET.indexOf(char);
|
|
63
|
+
if (value === -1) {
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
symbols.push(value & SYMBOL_MASK);
|
|
67
|
+
groupAccumulator = groupAccumulator * GROUP_SIZE + (value >> GROUP_BITS);
|
|
68
|
+
groupCount += 1;
|
|
69
|
+
if (groupCount === GROUP_SIZE) {
|
|
70
|
+
symbols.push(groupAccumulator);
|
|
71
|
+
groupAccumulator = 0;
|
|
72
|
+
groupCount = 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (groupCount > 0) {
|
|
76
|
+
symbols.push(groupAccumulator);
|
|
77
|
+
}
|
|
78
|
+
return symbols;
|
|
79
|
+
}
|
|
80
|
+
function checksumCreate(script) {
|
|
81
|
+
const symbols = expand(script);
|
|
82
|
+
if (symbols === void 0) {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
const checksum = polymod([...symbols, 0, 0, 0, 0, 0, 0, 0, 0]) ^ 1n;
|
|
86
|
+
let result = "";
|
|
87
|
+
for (let index = 0; index < CHECKSUM_LENGTH; index += 1) {
|
|
88
|
+
const shift = BigInt(GROUP_BITS * (CHECKSUM_LENGTH - 1 - index));
|
|
89
|
+
const symbolIndex = Number(checksum >> shift & BigInt(SYMBOL_MASK));
|
|
90
|
+
result += CHECKSUM_CHARSET[symbolIndex];
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
function checksumVerify(script, checksum) {
|
|
95
|
+
if (checksum.length !== CHECKSUM_LENGTH) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const scriptSymbols = expand(script);
|
|
99
|
+
if (scriptSymbols === void 0) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const checksumSymbols = [];
|
|
103
|
+
for (const char of checksum) {
|
|
104
|
+
const value = CHECKSUM_CHARSET.indexOf(char);
|
|
105
|
+
if (value === -1) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
checksumSymbols.push(value);
|
|
109
|
+
}
|
|
110
|
+
return polymod([...scriptSymbols, ...checksumSymbols]) === 1n;
|
|
111
|
+
}
|
|
112
|
+
function hasValidCharset(text) {
|
|
113
|
+
return expand(text) !== void 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/base58.ts
|
|
117
|
+
var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
118
|
+
var BASE = 58n;
|
|
119
|
+
var BYTE_MAX_PLUS_ONE = 256n;
|
|
120
|
+
var CHAR_TO_VALUE = new Map(
|
|
121
|
+
ALPHABET.split("").map((char, index) => [char, BigInt(index)])
|
|
122
|
+
);
|
|
123
|
+
function base58Decode(text) {
|
|
124
|
+
if (text.length === 0) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
let value = 0n;
|
|
128
|
+
for (const char of text) {
|
|
129
|
+
const digit = CHAR_TO_VALUE.get(char);
|
|
130
|
+
if (digit === void 0) {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
value = value * BASE + digit;
|
|
134
|
+
}
|
|
135
|
+
const bytes = [];
|
|
136
|
+
while (value > 0n) {
|
|
137
|
+
bytes.unshift(Number(value % BYTE_MAX_PLUS_ONE));
|
|
138
|
+
value /= BYTE_MAX_PLUS_ONE;
|
|
139
|
+
}
|
|
140
|
+
for (const char of text) {
|
|
141
|
+
if (char !== "1") {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
bytes.unshift(0);
|
|
145
|
+
}
|
|
146
|
+
return Uint8Array.from(bytes);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/key-expression.ts
|
|
150
|
+
var MAX_INDEX = 2147483647;
|
|
151
|
+
var FINGERPRINT_PATTERN = /^[0-9a-fA-F]{8}$/;
|
|
152
|
+
var STEP_PATTERN = /^([0-9]+)(h|')?$/;
|
|
153
|
+
var EXTENDED_KEY_BYTES = 82;
|
|
154
|
+
var WIF_UNCOMPRESSED_BYTES = 37;
|
|
155
|
+
var WIF_COMPRESSED_BYTES = 38;
|
|
156
|
+
var XPUB_VERSIONS = {
|
|
157
|
+
xpub: 76067358,
|
|
158
|
+
xprv: 76066276,
|
|
159
|
+
tpub: 70617039,
|
|
160
|
+
tprv: 70615956
|
|
161
|
+
};
|
|
162
|
+
function fail(message) {
|
|
163
|
+
return { ok: false, message };
|
|
164
|
+
}
|
|
165
|
+
function parseStep(segment) {
|
|
166
|
+
const match = STEP_PATTERN.exec(segment);
|
|
167
|
+
if (match === null) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const [, digits, marker] = match;
|
|
171
|
+
const index = Number(digits);
|
|
172
|
+
if (!Number.isSafeInteger(index) || index > MAX_INDEX) {
|
|
173
|
+
return void 0;
|
|
174
|
+
}
|
|
175
|
+
return { index, hardened: marker !== void 0 };
|
|
176
|
+
}
|
|
177
|
+
function parseOrigin(bracketed) {
|
|
178
|
+
const segments = bracketed.split("/");
|
|
179
|
+
const [fingerprint, ...pathSegments] = segments;
|
|
180
|
+
if (fingerprint === void 0 || !FINGERPRINT_PATTERN.test(fingerprint)) {
|
|
181
|
+
return void 0;
|
|
182
|
+
}
|
|
183
|
+
const path = [];
|
|
184
|
+
for (const segment of pathSegments) {
|
|
185
|
+
const step = parseStep(segment);
|
|
186
|
+
if (step === void 0) {
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
path.push(step);
|
|
190
|
+
}
|
|
191
|
+
return { fingerprint: fingerprint.toLowerCase(), path };
|
|
192
|
+
}
|
|
193
|
+
function splitOrigin(text) {
|
|
194
|
+
if (!text.startsWith("[")) {
|
|
195
|
+
return { remainder: text };
|
|
196
|
+
}
|
|
197
|
+
const closeIndex = text.indexOf("]");
|
|
198
|
+
if (closeIndex === -1) {
|
|
199
|
+
return fail("unterminated key origin: missing ']'");
|
|
200
|
+
}
|
|
201
|
+
const origin = parseOrigin(text.slice(1, closeIndex));
|
|
202
|
+
if (origin === void 0) {
|
|
203
|
+
return fail(
|
|
204
|
+
"invalid key origin: expected an 8-character hex fingerprint and /NUM or /NUMh steps"
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const remainder = text.slice(closeIndex + 1);
|
|
208
|
+
if (remainder.startsWith("[")) {
|
|
209
|
+
return fail("a key expression may only carry one key origin");
|
|
210
|
+
}
|
|
211
|
+
if (remainder.length === 0) {
|
|
212
|
+
return fail("key origin is not followed by a key");
|
|
213
|
+
}
|
|
214
|
+
return { origin, remainder };
|
|
215
|
+
}
|
|
216
|
+
function parseTrailingPath(text) {
|
|
217
|
+
if (text.length === 0) {
|
|
218
|
+
return { path: [], isRanged: false };
|
|
219
|
+
}
|
|
220
|
+
const segments = text.slice(1).split("/");
|
|
221
|
+
const path = [];
|
|
222
|
+
for (const [index, segment] of segments.entries()) {
|
|
223
|
+
const isLast = index === segments.length - 1;
|
|
224
|
+
if (isLast && /^\*(h|')?$/.test(segment)) {
|
|
225
|
+
return { path, isRanged: true };
|
|
226
|
+
}
|
|
227
|
+
if (segment.startsWith("<") && segment.endsWith(">")) {
|
|
228
|
+
const values = segment.slice(1, -1).split(";").map((part) => parseStep(part));
|
|
229
|
+
if (values.length < 2 || values.some((value) => value === void 0)) {
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
const resolved = values;
|
|
233
|
+
const seen = /* @__PURE__ */ new Set();
|
|
234
|
+
for (const value of resolved) {
|
|
235
|
+
const key = `${String(value.index)}:${String(value.hardened)}`;
|
|
236
|
+
if (seen.has(key)) {
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
seen.add(key);
|
|
240
|
+
}
|
|
241
|
+
path.push({ kind: "multipath", values: resolved });
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const step = parseStep(segment);
|
|
245
|
+
if (step === void 0) {
|
|
246
|
+
return void 0;
|
|
247
|
+
}
|
|
248
|
+
path.push({ kind: "step", ...step });
|
|
249
|
+
}
|
|
250
|
+
return { path, isRanged: false };
|
|
251
|
+
}
|
|
252
|
+
function classifyKeyMaterial(keyBody) {
|
|
253
|
+
if (/^(02|03)[0-9a-fA-F]{64}$/.test(keyBody)) {
|
|
254
|
+
return "hex-compressed";
|
|
255
|
+
}
|
|
256
|
+
if (/^04[0-9a-fA-F]{128}$/.test(keyBody)) {
|
|
257
|
+
return "hex-uncompressed";
|
|
258
|
+
}
|
|
259
|
+
if (/^[0-9a-fA-F]{64}$/.test(keyBody)) {
|
|
260
|
+
return "hex-xonly";
|
|
261
|
+
}
|
|
262
|
+
const extendedPrefix = keyBody.slice(0, 4);
|
|
263
|
+
if (extendedPrefix in XPUB_VERSIONS) {
|
|
264
|
+
const decoded2 = base58Decode(keyBody);
|
|
265
|
+
const expectedVersion = XPUB_VERSIONS[extendedPrefix];
|
|
266
|
+
if (decoded2?.length === EXTENDED_KEY_BYTES && expectedVersion !== void 0) {
|
|
267
|
+
const version = decoded2[0] << 24 | decoded2[1] << 16 | decoded2[2] << 8 | decoded2[3];
|
|
268
|
+
if (version >>> 0 === expectedVersion >>> 0) {
|
|
269
|
+
return extendedPrefix === "xprv" || extendedPrefix === "tprv" ? "xprv" : "xpub";
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
const decoded = base58Decode(keyBody);
|
|
275
|
+
if (decoded?.length === WIF_COMPRESSED_BYTES) {
|
|
276
|
+
return "wif-compressed";
|
|
277
|
+
}
|
|
278
|
+
if (decoded?.length === WIF_UNCOMPRESSED_BYTES) {
|
|
279
|
+
return "wif-uncompressed";
|
|
280
|
+
}
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
function parseKeyExpression(raw, options) {
|
|
284
|
+
const split = splitOrigin(raw);
|
|
285
|
+
if ("ok" in split) {
|
|
286
|
+
return split;
|
|
287
|
+
}
|
|
288
|
+
const { origin, remainder } = split;
|
|
289
|
+
const slashIndex = remainder.indexOf("/");
|
|
290
|
+
const keyBody = slashIndex === -1 ? remainder : remainder.slice(0, slashIndex);
|
|
291
|
+
const trailingText = slashIndex === -1 ? "" : remainder.slice(slashIndex);
|
|
292
|
+
const kind = classifyKeyMaterial(keyBody);
|
|
293
|
+
if (kind === void 0) {
|
|
294
|
+
return fail(`unrecognized key material: ${keyBody}`);
|
|
295
|
+
}
|
|
296
|
+
if (kind === "hex-xonly" && !options.allowXOnly) {
|
|
297
|
+
return fail("bare x-only (64 hex character) keys are only valid inside tr()");
|
|
298
|
+
}
|
|
299
|
+
if (kind !== "xpub" && kind !== "xprv" && trailingText.length > 0) {
|
|
300
|
+
return fail("a derivation path is only allowed after an extended (xpub/xprv) key");
|
|
301
|
+
}
|
|
302
|
+
const trailing = parseTrailingPath(trailingText);
|
|
303
|
+
if (trailing === void 0) {
|
|
304
|
+
return fail(`invalid derivation path: ${trailingText}`);
|
|
305
|
+
}
|
|
306
|
+
const key = {
|
|
307
|
+
raw,
|
|
308
|
+
kind,
|
|
309
|
+
...origin !== void 0 ? { origin } : {},
|
|
310
|
+
path: trailing.path,
|
|
311
|
+
isRanged: trailing.isRanged
|
|
312
|
+
};
|
|
313
|
+
return { ok: true, key };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/traverse.ts
|
|
317
|
+
function forEachKey(node, visit) {
|
|
318
|
+
switch (node.kind) {
|
|
319
|
+
case "pk":
|
|
320
|
+
case "pkh":
|
|
321
|
+
case "wpkh":
|
|
322
|
+
case "combo":
|
|
323
|
+
visit(node.key);
|
|
324
|
+
break;
|
|
325
|
+
case "sh":
|
|
326
|
+
case "wsh":
|
|
327
|
+
forEachKey(node.inner, visit);
|
|
328
|
+
break;
|
|
329
|
+
case "multi":
|
|
330
|
+
node.keys.forEach(visit);
|
|
331
|
+
break;
|
|
332
|
+
case "raw":
|
|
333
|
+
case "addr":
|
|
334
|
+
break;
|
|
335
|
+
case "tr":
|
|
336
|
+
visit(node.internalKey);
|
|
337
|
+
if (node.tree !== void 0) {
|
|
338
|
+
forEachKeyInTree(node.tree, visit);
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function forEachKeyInTree(tree, visit) {
|
|
344
|
+
if (tree.kind === "leaf") {
|
|
345
|
+
visit(tree.script.key);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
forEachKeyInTree(tree.left, visit);
|
|
349
|
+
forEachKeyInTree(tree.right, visit);
|
|
350
|
+
}
|
|
351
|
+
function anyKeyRanged(node) {
|
|
352
|
+
let ranged = false;
|
|
353
|
+
forEachKey(node, (key) => {
|
|
354
|
+
if (key.isRanged) {
|
|
355
|
+
ranged = true;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
return ranged;
|
|
359
|
+
}
|
|
360
|
+
function collectTapLeaves(tree, depth = 0) {
|
|
361
|
+
if (tree.kind === "leaf") {
|
|
362
|
+
return [{ key: tree.script.key, depth }];
|
|
363
|
+
}
|
|
364
|
+
return [...collectTapLeaves(tree.left, depth + 1), ...collectTapLeaves(tree.right, depth + 1)];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/multipath.ts
|
|
368
|
+
function mapKeys(node, fn) {
|
|
369
|
+
switch (node.kind) {
|
|
370
|
+
case "pk":
|
|
371
|
+
case "pkh":
|
|
372
|
+
case "wpkh":
|
|
373
|
+
case "combo":
|
|
374
|
+
return { ...node, key: fn(node.key) };
|
|
375
|
+
case "sh":
|
|
376
|
+
case "wsh":
|
|
377
|
+
return { ...node, inner: mapKeys(node.inner, fn) };
|
|
378
|
+
case "multi":
|
|
379
|
+
return { ...node, keys: node.keys.map(fn) };
|
|
380
|
+
case "tr":
|
|
381
|
+
return {
|
|
382
|
+
...node,
|
|
383
|
+
internalKey: fn(node.internalKey),
|
|
384
|
+
...node.tree !== void 0 ? { tree: mapTapTree(node.tree, fn) } : {}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function mapTapTree(tree, fn) {
|
|
389
|
+
if (tree.kind === "leaf") {
|
|
390
|
+
const leaf = { kind: "pk", key: fn(tree.script.key) };
|
|
391
|
+
return { kind: "leaf", script: leaf };
|
|
392
|
+
}
|
|
393
|
+
return { kind: "branch", left: mapTapTree(tree.left, fn), right: mapTapTree(tree.right, fn) };
|
|
394
|
+
}
|
|
395
|
+
function replaceMultipathAt(key, index) {
|
|
396
|
+
const elementIndex = key.path.findIndex((element) => element.kind === "multipath");
|
|
397
|
+
if (elementIndex === -1) {
|
|
398
|
+
return key;
|
|
399
|
+
}
|
|
400
|
+
const path = key.path.map((element, position) => {
|
|
401
|
+
if (position !== elementIndex || element.kind !== "multipath") {
|
|
402
|
+
return element;
|
|
403
|
+
}
|
|
404
|
+
const value = element.values[index];
|
|
405
|
+
return { kind: "step", ...value };
|
|
406
|
+
});
|
|
407
|
+
return { ...key, path };
|
|
408
|
+
}
|
|
409
|
+
function expandMultipath(script) {
|
|
410
|
+
const lengths = /* @__PURE__ */ new Set();
|
|
411
|
+
forEachKey(script, (key) => {
|
|
412
|
+
for (const element of key.path) {
|
|
413
|
+
if (element.kind === "multipath") {
|
|
414
|
+
lengths.add(element.values.length);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
if (lengths.size === 0) {
|
|
419
|
+
return { ok: true, descriptors: [script] };
|
|
420
|
+
}
|
|
421
|
+
if (lengths.size > 1) {
|
|
422
|
+
return {
|
|
423
|
+
ok: false,
|
|
424
|
+
message: "every multipath element in a descriptor must have the same number of values"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const [length] = [...lengths];
|
|
428
|
+
const descriptors = [];
|
|
429
|
+
for (let index = 0; index < length; index += 1) {
|
|
430
|
+
descriptors.push(mapKeys(script, (key) => replaceMultipathAt(key, index)));
|
|
431
|
+
}
|
|
432
|
+
return { ok: true, descriptors };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// src/tokenizer.ts
|
|
436
|
+
var STRUCTURAL = {
|
|
437
|
+
"(": "lparen",
|
|
438
|
+
")": "rparen",
|
|
439
|
+
",": "comma",
|
|
440
|
+
"{": "lbrace",
|
|
441
|
+
"}": "rbrace"
|
|
442
|
+
};
|
|
443
|
+
function tokenize(text) {
|
|
444
|
+
const tokens = [];
|
|
445
|
+
let index = 0;
|
|
446
|
+
while (index < text.length) {
|
|
447
|
+
const char = text[index];
|
|
448
|
+
const structuralKind = STRUCTURAL[char];
|
|
449
|
+
if (structuralKind !== void 0) {
|
|
450
|
+
tokens.push({ kind: structuralKind, text: char, position: index });
|
|
451
|
+
index += 1;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const start = index;
|
|
455
|
+
let atom = "";
|
|
456
|
+
while (index < text.length && STRUCTURAL[text[index]] === void 0) {
|
|
457
|
+
const atomChar = text[index];
|
|
458
|
+
if (!hasValidCharset(atomChar)) {
|
|
459
|
+
return { ok: false, position: index };
|
|
460
|
+
}
|
|
461
|
+
atom += atomChar;
|
|
462
|
+
index += 1;
|
|
463
|
+
}
|
|
464
|
+
tokens.push({ kind: "atom", text: atom, position: start });
|
|
465
|
+
}
|
|
466
|
+
return { ok: true, tokens };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/parse.ts
|
|
470
|
+
var CHECKSUM_LENGTH2 = 8;
|
|
471
|
+
var HEX_PAIR = 2;
|
|
472
|
+
function keyLimit(context) {
|
|
473
|
+
if (context.place === "top") {
|
|
474
|
+
return 3;
|
|
475
|
+
}
|
|
476
|
+
if (context.place === "sh") {
|
|
477
|
+
return 15;
|
|
478
|
+
}
|
|
479
|
+
return 20;
|
|
480
|
+
}
|
|
481
|
+
function requireCompressed(context) {
|
|
482
|
+
return context.place === "wsh";
|
|
483
|
+
}
|
|
484
|
+
function isCompressedCapable(key) {
|
|
485
|
+
return key.kind === "hex-compressed" || key.kind === "wif-compressed" || key.kind === "xpub" || key.kind === "xprv";
|
|
486
|
+
}
|
|
487
|
+
var Cursor = class {
|
|
488
|
+
constructor(tokens) {
|
|
489
|
+
__publicField(this, "tokens", tokens);
|
|
490
|
+
__publicField(this, "position", 0);
|
|
491
|
+
}
|
|
492
|
+
peek() {
|
|
493
|
+
return this.tokens[this.position];
|
|
494
|
+
}
|
|
495
|
+
advance() {
|
|
496
|
+
const token = this.tokens[this.position];
|
|
497
|
+
this.position += 1;
|
|
498
|
+
return token;
|
|
499
|
+
}
|
|
500
|
+
atEnd() {
|
|
501
|
+
return this.position >= this.tokens.length;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
function fail2(reason, message, position) {
|
|
505
|
+
return { ok: false, reason, message, ...position !== void 0 ? { position } : {} };
|
|
506
|
+
}
|
|
507
|
+
function expectKind(cursor, kind, what) {
|
|
508
|
+
const token = cursor.advance();
|
|
509
|
+
if (token === void 0) {
|
|
510
|
+
return fail2("unexpected-token", `expected ${what} but reached the end of the descriptor`);
|
|
511
|
+
}
|
|
512
|
+
if (token.kind !== kind) {
|
|
513
|
+
return fail2("unexpected-token", `expected ${what}`, token.position);
|
|
514
|
+
}
|
|
515
|
+
return { ok: true, token };
|
|
516
|
+
}
|
|
517
|
+
function isFailure(value) {
|
|
518
|
+
return !value.ok;
|
|
519
|
+
}
|
|
520
|
+
function parseKey(cursor, allowXOnly) {
|
|
521
|
+
const token = cursor.advance();
|
|
522
|
+
if (token === void 0) {
|
|
523
|
+
return fail2(
|
|
524
|
+
"unexpected-token",
|
|
525
|
+
"expected a key expression but reached the end of the descriptor"
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
if (token.kind !== "atom") {
|
|
529
|
+
return fail2("unexpected-token", "expected a key expression", token.position);
|
|
530
|
+
}
|
|
531
|
+
const result = parseKeyExpression(token.text, { allowXOnly });
|
|
532
|
+
if (!result.ok) {
|
|
533
|
+
return fail2("invalid-key-expression", result.message, token.position);
|
|
534
|
+
}
|
|
535
|
+
return { ok: true, key: result.key };
|
|
536
|
+
}
|
|
537
|
+
function parseNumber(cursor) {
|
|
538
|
+
const token = cursor.advance();
|
|
539
|
+
if (token === void 0) {
|
|
540
|
+
return fail2("unexpected-token", "expected a number but reached the end of the descriptor");
|
|
541
|
+
}
|
|
542
|
+
if (token.kind !== "atom" || !/^[0-9]+$/.test(token.text) || !Number.isSafeInteger(Number(token.text))) {
|
|
543
|
+
return fail2("unexpected-token", "expected a number", token.position);
|
|
544
|
+
}
|
|
545
|
+
return { ok: true, value: Number(token.text) };
|
|
546
|
+
}
|
|
547
|
+
function parseSingleKeyNode(cursor, kind, context, position) {
|
|
548
|
+
const keyResult = parseKey(cursor, false);
|
|
549
|
+
if (isFailure(keyResult)) {
|
|
550
|
+
return keyResult;
|
|
551
|
+
}
|
|
552
|
+
if ((kind === "wpkh" || requireCompressed(context)) && !isCompressedCapable(keyResult.key)) {
|
|
553
|
+
return fail2("invalid-key-expression", `${kind}() requires a compressed key`, position);
|
|
554
|
+
}
|
|
555
|
+
const closeResult = expectKind(cursor, "rparen", `')' to close ${kind}(...)`);
|
|
556
|
+
if (isFailure(closeResult)) {
|
|
557
|
+
return closeResult;
|
|
558
|
+
}
|
|
559
|
+
return { ok: true, node: { kind, key: keyResult.key } };
|
|
560
|
+
}
|
|
561
|
+
function parseMultiNode(cursor, sorted, context, position) {
|
|
562
|
+
const thresholdResult = parseNumber(cursor);
|
|
563
|
+
if (isFailure(thresholdResult)) {
|
|
564
|
+
return thresholdResult;
|
|
565
|
+
}
|
|
566
|
+
const keys = [];
|
|
567
|
+
for (; ; ) {
|
|
568
|
+
const comma = expectKind(cursor, "comma", "',' before a key in multi(...)");
|
|
569
|
+
if (isFailure(comma)) {
|
|
570
|
+
return comma;
|
|
571
|
+
}
|
|
572
|
+
const keyResult = parseKey(cursor, false);
|
|
573
|
+
if (isFailure(keyResult)) {
|
|
574
|
+
return keyResult;
|
|
575
|
+
}
|
|
576
|
+
if (requireCompressed(context) && !isCompressedCapable(keyResult.key)) {
|
|
577
|
+
return fail2("invalid-key-expression", "keys inside wsh() must be compressed", position);
|
|
578
|
+
}
|
|
579
|
+
keys.push(keyResult.key);
|
|
580
|
+
if (cursor.peek()?.kind !== "comma") {
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close multi(...)");
|
|
585
|
+
if (isFailure(closeResult)) {
|
|
586
|
+
return closeResult;
|
|
587
|
+
}
|
|
588
|
+
const limit = keyLimit(context);
|
|
589
|
+
if (keys.length > limit) {
|
|
590
|
+
return fail2(
|
|
591
|
+
"key-count-exceeded",
|
|
592
|
+
`at most ${String(limit)} keys are allowed in this context`,
|
|
593
|
+
position
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
if (thresholdResult.value < 1 || thresholdResult.value > keys.length) {
|
|
597
|
+
return fail2("invalid-context", "threshold must be between 1 and the number of keys", position);
|
|
598
|
+
}
|
|
599
|
+
return { ok: true, node: { kind: "multi", threshold: thresholdResult.value, keys, sorted } };
|
|
600
|
+
}
|
|
601
|
+
function parseTapLeaf(cursor) {
|
|
602
|
+
const nameToken = cursor.advance();
|
|
603
|
+
if (nameToken === void 0) {
|
|
604
|
+
return fail2(
|
|
605
|
+
"unexpected-token",
|
|
606
|
+
"expected a tapscript leaf but reached the end of the descriptor"
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
if (nameToken.kind !== "atom" || nameToken.text !== "pk") {
|
|
610
|
+
return fail2(
|
|
611
|
+
"unsupported",
|
|
612
|
+
"tapscript leaves are limited to pk(KEY) in this version; other miniscript fragments are not yet supported",
|
|
613
|
+
nameToken.position
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
const lparen = expectKind(cursor, "lparen", "'(' after pk");
|
|
617
|
+
if (isFailure(lparen)) {
|
|
618
|
+
return lparen;
|
|
619
|
+
}
|
|
620
|
+
const keyResult = parseKey(cursor, true);
|
|
621
|
+
if (isFailure(keyResult)) {
|
|
622
|
+
return keyResult;
|
|
623
|
+
}
|
|
624
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close pk(...)");
|
|
625
|
+
if (isFailure(closeResult)) {
|
|
626
|
+
return closeResult;
|
|
627
|
+
}
|
|
628
|
+
const leaf = { kind: "pk", key: keyResult.key };
|
|
629
|
+
return { ok: true, tree: { kind: "leaf", script: leaf } };
|
|
630
|
+
}
|
|
631
|
+
function parseTapTree(cursor) {
|
|
632
|
+
if (cursor.peek()?.kind === "lbrace") {
|
|
633
|
+
cursor.advance();
|
|
634
|
+
const leftResult = parseTapTree(cursor);
|
|
635
|
+
if (isFailure(leftResult)) {
|
|
636
|
+
return leftResult;
|
|
637
|
+
}
|
|
638
|
+
const comma = expectKind(cursor, "comma", "',' inside a tapscript tree branch");
|
|
639
|
+
if (isFailure(comma)) {
|
|
640
|
+
return comma;
|
|
641
|
+
}
|
|
642
|
+
const rightResult = parseTapTree(cursor);
|
|
643
|
+
if (isFailure(rightResult)) {
|
|
644
|
+
return rightResult;
|
|
645
|
+
}
|
|
646
|
+
const closeResult = expectKind(cursor, "rbrace", "'}' to close a tapscript tree branch");
|
|
647
|
+
if (isFailure(closeResult)) {
|
|
648
|
+
return closeResult;
|
|
649
|
+
}
|
|
650
|
+
return { ok: true, tree: { kind: "branch", left: leftResult.tree, right: rightResult.tree } };
|
|
651
|
+
}
|
|
652
|
+
return parseTapLeaf(cursor);
|
|
653
|
+
}
|
|
654
|
+
function parseTr(cursor) {
|
|
655
|
+
const keyResult = parseKey(cursor, true);
|
|
656
|
+
if (isFailure(keyResult)) {
|
|
657
|
+
return keyResult;
|
|
658
|
+
}
|
|
659
|
+
if (cursor.peek()?.kind === "rparen") {
|
|
660
|
+
cursor.advance();
|
|
661
|
+
return { ok: true, node: { kind: "tr", internalKey: keyResult.key } };
|
|
662
|
+
}
|
|
663
|
+
const comma = expectKind(cursor, "comma", "',' before the tapscript tree in tr(...)");
|
|
664
|
+
if (isFailure(comma)) {
|
|
665
|
+
return comma;
|
|
666
|
+
}
|
|
667
|
+
const treeResult = parseTapTree(cursor);
|
|
668
|
+
if (isFailure(treeResult)) {
|
|
669
|
+
return treeResult;
|
|
670
|
+
}
|
|
671
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close tr(...)");
|
|
672
|
+
if (isFailure(closeResult)) {
|
|
673
|
+
return closeResult;
|
|
674
|
+
}
|
|
675
|
+
return { ok: true, node: { kind: "tr", internalKey: keyResult.key, tree: treeResult.tree } };
|
|
676
|
+
}
|
|
677
|
+
function parseRaw(cursor, position) {
|
|
678
|
+
const token = cursor.advance();
|
|
679
|
+
if (token === void 0) {
|
|
680
|
+
return fail2("unexpected-token", "expected hex data in raw(...)", position);
|
|
681
|
+
}
|
|
682
|
+
if (token.kind !== "atom" || !/^[0-9a-fA-F]+$/.test(token.text) || token.text.length % HEX_PAIR !== 0) {
|
|
683
|
+
return fail2("invalid-context", "raw() requires an even-length hex string", token.position);
|
|
684
|
+
}
|
|
685
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close raw(...)");
|
|
686
|
+
if (isFailure(closeResult)) {
|
|
687
|
+
return closeResult;
|
|
688
|
+
}
|
|
689
|
+
return { ok: true, node: { kind: "raw", hex: token.text.toLowerCase() } };
|
|
690
|
+
}
|
|
691
|
+
function parseAddr(cursor, position) {
|
|
692
|
+
const token = cursor.advance();
|
|
693
|
+
if (token?.kind !== "atom") {
|
|
694
|
+
return fail2("unexpected-token", "expected an address in addr(...)", position);
|
|
695
|
+
}
|
|
696
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close addr(...)");
|
|
697
|
+
if (isFailure(closeResult)) {
|
|
698
|
+
return closeResult;
|
|
699
|
+
}
|
|
700
|
+
return { ok: true, node: { kind: "addr", address: token.text } };
|
|
701
|
+
}
|
|
702
|
+
function parseScript(cursor, context) {
|
|
703
|
+
const nameToken = cursor.advance();
|
|
704
|
+
if (nameToken === void 0) {
|
|
705
|
+
return fail2(
|
|
706
|
+
"unexpected-token",
|
|
707
|
+
"expected a script expression but reached the end of the descriptor"
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
if (nameToken.kind !== "atom") {
|
|
711
|
+
return fail2("unexpected-token", "expected a function name", nameToken.position);
|
|
712
|
+
}
|
|
713
|
+
const name = nameToken.text;
|
|
714
|
+
const position = nameToken.position;
|
|
715
|
+
const lparen = expectKind(cursor, "lparen", `'(' after ${name}`);
|
|
716
|
+
if (isFailure(lparen)) {
|
|
717
|
+
return lparen;
|
|
718
|
+
}
|
|
719
|
+
switch (name) {
|
|
720
|
+
case "pk":
|
|
721
|
+
case "pkh":
|
|
722
|
+
return parseSingleKeyNode(cursor, name, context, position);
|
|
723
|
+
case "wpkh": {
|
|
724
|
+
if (context.place === "wsh") {
|
|
725
|
+
return fail2("invalid-context", "wpkh() is not allowed inside wsh()", position);
|
|
726
|
+
}
|
|
727
|
+
return parseSingleKeyNode(cursor, "wpkh", context, position);
|
|
728
|
+
}
|
|
729
|
+
case "sh": {
|
|
730
|
+
if (context.place !== "top") {
|
|
731
|
+
return fail2("invalid-context", "sh() is only allowed at the top level", position);
|
|
732
|
+
}
|
|
733
|
+
const innerResult = parseScript(cursor, { place: "sh" });
|
|
734
|
+
if (isFailure(innerResult)) {
|
|
735
|
+
return innerResult;
|
|
736
|
+
}
|
|
737
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close sh(...)");
|
|
738
|
+
if (isFailure(closeResult)) {
|
|
739
|
+
return closeResult;
|
|
740
|
+
}
|
|
741
|
+
return { ok: true, node: { kind: "sh", inner: innerResult.node } };
|
|
742
|
+
}
|
|
743
|
+
case "wsh": {
|
|
744
|
+
if (context.place === "wsh") {
|
|
745
|
+
return fail2("invalid-context", "wsh() cannot be nested inside wsh()", position);
|
|
746
|
+
}
|
|
747
|
+
const innerResult = parseScript(cursor, { place: "wsh" });
|
|
748
|
+
if (isFailure(innerResult)) {
|
|
749
|
+
return innerResult;
|
|
750
|
+
}
|
|
751
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close wsh(...)");
|
|
752
|
+
if (isFailure(closeResult)) {
|
|
753
|
+
return closeResult;
|
|
754
|
+
}
|
|
755
|
+
return { ok: true, node: { kind: "wsh", inner: innerResult.node } };
|
|
756
|
+
}
|
|
757
|
+
case "multi":
|
|
758
|
+
case "sortedmulti":
|
|
759
|
+
return parseMultiNode(cursor, name === "sortedmulti", context, position);
|
|
760
|
+
case "combo": {
|
|
761
|
+
if (context.place !== "top") {
|
|
762
|
+
return fail2("invalid-context", "combo() is only allowed at the top level", position);
|
|
763
|
+
}
|
|
764
|
+
const keyResult = parseKey(cursor, false);
|
|
765
|
+
if (isFailure(keyResult)) {
|
|
766
|
+
return keyResult;
|
|
767
|
+
}
|
|
768
|
+
const closeResult = expectKind(cursor, "rparen", "')' to close combo(...)");
|
|
769
|
+
if (isFailure(closeResult)) {
|
|
770
|
+
return closeResult;
|
|
771
|
+
}
|
|
772
|
+
return { ok: true, node: { kind: "combo", key: keyResult.key } };
|
|
773
|
+
}
|
|
774
|
+
case "raw": {
|
|
775
|
+
if (context.place !== "top") {
|
|
776
|
+
return fail2("invalid-context", "raw() is only allowed at the top level", position);
|
|
777
|
+
}
|
|
778
|
+
return parseRaw(cursor, position);
|
|
779
|
+
}
|
|
780
|
+
case "addr": {
|
|
781
|
+
if (context.place !== "top") {
|
|
782
|
+
return fail2("invalid-context", "addr() is only allowed at the top level", position);
|
|
783
|
+
}
|
|
784
|
+
return parseAddr(cursor, position);
|
|
785
|
+
}
|
|
786
|
+
case "tr": {
|
|
787
|
+
if (context.place !== "top") {
|
|
788
|
+
return fail2("invalid-context", "tr() is only allowed at the top level", position);
|
|
789
|
+
}
|
|
790
|
+
return parseTr(cursor);
|
|
791
|
+
}
|
|
792
|
+
case "musig":
|
|
793
|
+
return fail2("unsupported", "musig() (BIP-390) is not implemented", position);
|
|
794
|
+
case "sp":
|
|
795
|
+
return fail2("unsupported", "sp() (BIP-392, silent payments) is not implemented", position);
|
|
796
|
+
default:
|
|
797
|
+
return fail2("unknown-function", `unknown script expression: ${name}`, position);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function parseDescriptor(text) {
|
|
801
|
+
const hashIndex = text.indexOf("#");
|
|
802
|
+
const scriptText = hashIndex === -1 ? text : text.slice(0, hashIndex);
|
|
803
|
+
const tokenizeResult = tokenize(scriptText);
|
|
804
|
+
if (!tokenizeResult.ok) {
|
|
805
|
+
return fail2(
|
|
806
|
+
"invalid-character",
|
|
807
|
+
"the descriptor contains a character outside the allowed charset",
|
|
808
|
+
tokenizeResult.position
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
if (hashIndex !== -1) {
|
|
812
|
+
const checksumText = text.slice(hashIndex + 1);
|
|
813
|
+
if (checksumText.length !== CHECKSUM_LENGTH2 || !checksumVerify(scriptText, checksumText)) {
|
|
814
|
+
return fail2("invalid-checksum", "the descriptor checksum does not match its script");
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
const cursor = new Cursor(tokenizeResult.tokens);
|
|
818
|
+
const scriptResult = parseScript(cursor, { place: "top" });
|
|
819
|
+
if (isFailure(scriptResult)) {
|
|
820
|
+
return scriptResult;
|
|
821
|
+
}
|
|
822
|
+
if (!cursor.atEnd()) {
|
|
823
|
+
return fail2(
|
|
824
|
+
"unexpected-token",
|
|
825
|
+
"unexpected trailing content after the descriptor",
|
|
826
|
+
cursor.peek()?.position
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
const descriptor = {
|
|
830
|
+
script: scriptResult.node,
|
|
831
|
+
...hashIndex !== -1 ? { checksum: text.slice(hashIndex + 1) } : {},
|
|
832
|
+
isRanged: anyKeyRanged(scriptResult.node)
|
|
833
|
+
};
|
|
834
|
+
return { ok: true, descriptor };
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/bech32.ts
|
|
838
|
+
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
839
|
+
var BECH32_CONST = 1;
|
|
840
|
+
var BECH32M_CONST = 734539939;
|
|
841
|
+
var GENERATOR2 = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
842
|
+
var CHECKSUM_LENGTH3 = 6;
|
|
843
|
+
var MIN_HRP_LENGTH = 1;
|
|
844
|
+
function polymod2(values) {
|
|
845
|
+
let checksum = 1;
|
|
846
|
+
for (const value of values) {
|
|
847
|
+
const top = checksum >>> 25;
|
|
848
|
+
checksum = (checksum & 33554431) << 5 ^ value;
|
|
849
|
+
for (let bit = 0; bit < GENERATOR2.length; bit += 1) {
|
|
850
|
+
const generatorValue = GENERATOR2[bit];
|
|
851
|
+
if (generatorValue !== void 0 && (top >>> bit & 1) === 1) {
|
|
852
|
+
checksum ^= generatorValue;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return checksum >>> 0;
|
|
857
|
+
}
|
|
858
|
+
function hrpExpand(hrp) {
|
|
859
|
+
const high = [];
|
|
860
|
+
const low = [];
|
|
861
|
+
for (const char of hrp) {
|
|
862
|
+
const code = char.codePointAt(0);
|
|
863
|
+
high.push(code >> 5);
|
|
864
|
+
low.push(code & 31);
|
|
865
|
+
}
|
|
866
|
+
return [...high, 0, ...low];
|
|
867
|
+
}
|
|
868
|
+
function convertBits(data, fromBits, toBits, pad) {
|
|
869
|
+
let accumulator = 0;
|
|
870
|
+
let bits = 0;
|
|
871
|
+
const result = [];
|
|
872
|
+
const maxValue = (1 << toBits) - 1;
|
|
873
|
+
for (const value of data) {
|
|
874
|
+
if (value < 0 || value >>> fromBits !== 0) {
|
|
875
|
+
return void 0;
|
|
876
|
+
}
|
|
877
|
+
accumulator = accumulator << fromBits | value;
|
|
878
|
+
bits += fromBits;
|
|
879
|
+
while (bits >= toBits) {
|
|
880
|
+
bits -= toBits;
|
|
881
|
+
result.push(accumulator >>> bits & maxValue);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (bits >= fromBits || (accumulator << toBits - bits & maxValue) !== 0) {
|
|
885
|
+
return void 0;
|
|
886
|
+
}
|
|
887
|
+
return result;
|
|
888
|
+
}
|
|
889
|
+
function bech32Decode(address) {
|
|
890
|
+
if (address !== address.toLowerCase() && address !== address.toUpperCase()) {
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
const lowered = address.toLowerCase();
|
|
894
|
+
const separatorIndex = lowered.lastIndexOf("1");
|
|
895
|
+
if (separatorIndex < MIN_HRP_LENGTH || lowered.length - separatorIndex - 1 < CHECKSUM_LENGTH3) {
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
const hrp = lowered.slice(0, separatorIndex);
|
|
899
|
+
const dataPart = lowered.slice(separatorIndex + 1);
|
|
900
|
+
const values = [];
|
|
901
|
+
for (const char of dataPart) {
|
|
902
|
+
const value = CHARSET.indexOf(char);
|
|
903
|
+
if (value === -1) {
|
|
904
|
+
return void 0;
|
|
905
|
+
}
|
|
906
|
+
values.push(value);
|
|
907
|
+
}
|
|
908
|
+
const checksum = polymod2([...hrpExpand(hrp), ...values]);
|
|
909
|
+
if (checksum !== BECH32_CONST && checksum !== BECH32M_CONST) {
|
|
910
|
+
return void 0;
|
|
911
|
+
}
|
|
912
|
+
const words = values.slice(0, values.length - CHECKSUM_LENGTH3);
|
|
913
|
+
const [witnessVersion, ...programWords] = words;
|
|
914
|
+
if (witnessVersion === void 0) {
|
|
915
|
+
return void 0;
|
|
916
|
+
}
|
|
917
|
+
const isBech32m = checksum === BECH32M_CONST;
|
|
918
|
+
if (witnessVersion === 0 === isBech32m) {
|
|
919
|
+
return void 0;
|
|
920
|
+
}
|
|
921
|
+
const program = convertBits(programWords, 5, 8);
|
|
922
|
+
if (program === void 0 || program.length < 2 || program.length > 40) {
|
|
923
|
+
return void 0;
|
|
924
|
+
}
|
|
925
|
+
return { hrp, witnessVersion, program: Uint8Array.from(program) };
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/weights.ts
|
|
929
|
+
var ECDSA_SINGLE_SIG_BYTES = 72;
|
|
930
|
+
var ECDSA_MULTISIG_SIG_BYTES = 73;
|
|
931
|
+
var SCHNORR_KEYPATH_SIG_BYTES = 64;
|
|
932
|
+
var SCHNORR_SCRIPTPATH_SIG_BYTES = 65;
|
|
933
|
+
var COMPRESSED_PUBKEY_BYTES = 33;
|
|
934
|
+
var UNCOMPRESSED_PUBKEY_BYTES = 65;
|
|
935
|
+
var XONLY_PUBKEY_BYTES = 32;
|
|
936
|
+
var P2PKH_SCRIPT_BYTES = 25;
|
|
937
|
+
var P2WPKH_SCRIPT_BYTES = 22;
|
|
938
|
+
var P2SH_SCRIPT_BYTES = 23;
|
|
939
|
+
var P2WSH_SCRIPT_BYTES = 34;
|
|
940
|
+
var P2TR_SCRIPT_BYTES = 34;
|
|
941
|
+
var BASE58_ADDRESS_BYTES = 25;
|
|
942
|
+
var LEGACY_TX_OVERHEAD_BYTES = 40;
|
|
943
|
+
var VOUT_OVERHEAD_BYTES = 8;
|
|
944
|
+
var WEIGHT_SCALE = 4;
|
|
945
|
+
var PUSHDATA_OP_MAX = 76;
|
|
946
|
+
var PUSHDATA_UINT8_MAX = 255;
|
|
947
|
+
var PUSHDATA_UINT16_MAX = 65535;
|
|
948
|
+
var COMPACT_SIZE_UINT16_MIN = 253;
|
|
949
|
+
var COMPACT_SIZE_UINT16_MAX = 65535;
|
|
950
|
+
var COMPACT_SIZE_UINT32_MAX = 4294967295;
|
|
951
|
+
function weightFail(reason, message) {
|
|
952
|
+
return { ok: false, reason, message };
|
|
953
|
+
}
|
|
954
|
+
function compactSizeBytes(value) {
|
|
955
|
+
if (value < COMPACT_SIZE_UINT16_MIN) {
|
|
956
|
+
return 1;
|
|
957
|
+
}
|
|
958
|
+
if (value <= COMPACT_SIZE_UINT16_MAX) {
|
|
959
|
+
return 3;
|
|
960
|
+
}
|
|
961
|
+
if (value <= COMPACT_SIZE_UINT32_MAX) {
|
|
962
|
+
return 5;
|
|
963
|
+
}
|
|
964
|
+
return 9;
|
|
965
|
+
}
|
|
966
|
+
function pushDataPrefixBytes(length) {
|
|
967
|
+
if (length < PUSHDATA_OP_MAX) {
|
|
968
|
+
return 1;
|
|
969
|
+
}
|
|
970
|
+
if (length <= PUSHDATA_UINT8_MAX) {
|
|
971
|
+
return 2;
|
|
972
|
+
}
|
|
973
|
+
if (length <= PUSHDATA_UINT16_MAX) {
|
|
974
|
+
return 3;
|
|
975
|
+
}
|
|
976
|
+
return 5;
|
|
977
|
+
}
|
|
978
|
+
function keyMaterialBytes(kind) {
|
|
979
|
+
switch (kind) {
|
|
980
|
+
case "hex-compressed":
|
|
981
|
+
case "wif-compressed":
|
|
982
|
+
case "xpub":
|
|
983
|
+
case "xprv":
|
|
984
|
+
return COMPRESSED_PUBKEY_BYTES;
|
|
985
|
+
case "hex-uncompressed":
|
|
986
|
+
case "wif-uncompressed":
|
|
987
|
+
return UNCOMPRESSED_PUBKEY_BYTES;
|
|
988
|
+
case "hex-xonly":
|
|
989
|
+
return XONLY_PUBKEY_BYTES;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function simpleOutputScriptBytes(node) {
|
|
993
|
+
switch (node.kind) {
|
|
994
|
+
case "pk":
|
|
995
|
+
return keyMaterialBytes(node.key.kind) + 2;
|
|
996
|
+
case "pkh":
|
|
997
|
+
return P2PKH_SCRIPT_BYTES;
|
|
998
|
+
case "wpkh":
|
|
999
|
+
return P2WPKH_SCRIPT_BYTES;
|
|
1000
|
+
case "sh":
|
|
1001
|
+
return P2SH_SCRIPT_BYTES;
|
|
1002
|
+
case "wsh":
|
|
1003
|
+
return P2WSH_SCRIPT_BYTES;
|
|
1004
|
+
case "tr":
|
|
1005
|
+
return P2TR_SCRIPT_BYTES;
|
|
1006
|
+
case "multi": {
|
|
1007
|
+
const perKey = node.keys.reduce((sum, key) => {
|
|
1008
|
+
const bytes = keyMaterialBytes(key.kind);
|
|
1009
|
+
return sum + pushDataPrefixBytes(bytes) + bytes;
|
|
1010
|
+
}, 0);
|
|
1011
|
+
return 1 + perKey + 2;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
function leafInputCost(node) {
|
|
1016
|
+
switch (node.kind) {
|
|
1017
|
+
case "pk": {
|
|
1018
|
+
const scriptSig = pushDataPrefixBytes(ECDSA_SINGLE_SIG_BYTES) + ECDSA_SINGLE_SIG_BYTES;
|
|
1019
|
+
return { scriptSig, witness: 0 };
|
|
1020
|
+
}
|
|
1021
|
+
case "pkh": {
|
|
1022
|
+
const keyBytes = keyMaterialBytes(node.key.kind);
|
|
1023
|
+
const scriptSig = pushDataPrefixBytes(ECDSA_SINGLE_SIG_BYTES) + ECDSA_SINGLE_SIG_BYTES + pushDataPrefixBytes(keyBytes) + keyBytes;
|
|
1024
|
+
return { scriptSig, witness: 0 };
|
|
1025
|
+
}
|
|
1026
|
+
case "wpkh": {
|
|
1027
|
+
const keyBytes = keyMaterialBytes(node.key.kind);
|
|
1028
|
+
const items = 2;
|
|
1029
|
+
const witness = compactSizeBytes(items) + compactSizeBytes(ECDSA_SINGLE_SIG_BYTES) + ECDSA_SINGLE_SIG_BYTES + compactSizeBytes(keyBytes) + keyBytes;
|
|
1030
|
+
return { scriptSig: 0, witness };
|
|
1031
|
+
}
|
|
1032
|
+
case "multi": {
|
|
1033
|
+
const sigCost = pushDataPrefixBytes(ECDSA_MULTISIG_SIG_BYTES) + ECDSA_MULTISIG_SIG_BYTES;
|
|
1034
|
+
return { scriptSig: 1 + node.threshold * sigCost, witness: 0 };
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function segwitWitnessBytes(node) {
|
|
1039
|
+
switch (node.kind) {
|
|
1040
|
+
case "pk": {
|
|
1041
|
+
const scriptBytes = simpleOutputScriptBytes(node);
|
|
1042
|
+
const items = 2;
|
|
1043
|
+
return compactSizeBytes(items) + compactSizeBytes(ECDSA_SINGLE_SIG_BYTES) + ECDSA_SINGLE_SIG_BYTES + compactSizeBytes(scriptBytes) + scriptBytes;
|
|
1044
|
+
}
|
|
1045
|
+
case "pkh": {
|
|
1046
|
+
const keyBytes = keyMaterialBytes(node.key.kind);
|
|
1047
|
+
const items = 3;
|
|
1048
|
+
return compactSizeBytes(items) + compactSizeBytes(ECDSA_SINGLE_SIG_BYTES) + ECDSA_SINGLE_SIG_BYTES + compactSizeBytes(keyBytes) + keyBytes + compactSizeBytes(P2PKH_SCRIPT_BYTES) + P2PKH_SCRIPT_BYTES;
|
|
1049
|
+
}
|
|
1050
|
+
case "multi": {
|
|
1051
|
+
const scriptBytes = simpleOutputScriptBytes(node);
|
|
1052
|
+
const sigCost = compactSizeBytes(ECDSA_MULTISIG_SIG_BYTES) + ECDSA_MULTISIG_SIG_BYTES;
|
|
1053
|
+
const items = node.threshold + 2;
|
|
1054
|
+
return compactSizeBytes(items) + compactSizeBytes(0) + node.threshold * sigCost + compactSizeBytes(scriptBytes) + scriptBytes;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
function computeTrCost(node) {
|
|
1059
|
+
const keyPathWitness = compactSizeBytes(1) + compactSizeBytes(SCHNORR_KEYPATH_SIG_BYTES) + SCHNORR_KEYPATH_SIG_BYTES;
|
|
1060
|
+
if (node.tree === void 0) {
|
|
1061
|
+
return { scriptSig: 0, witness: keyPathWitness };
|
|
1062
|
+
}
|
|
1063
|
+
const leafWitnesses = collectTapLeaves(node.tree).map(({ depth }) => {
|
|
1064
|
+
const leafScriptBytes = 1 + XONLY_PUBKEY_BYTES + 1;
|
|
1065
|
+
const controlBlockBytes = 1 + XONLY_PUBKEY_BYTES + XONLY_PUBKEY_BYTES * depth;
|
|
1066
|
+
const items = 3;
|
|
1067
|
+
return compactSizeBytes(items) + compactSizeBytes(SCHNORR_SCRIPTPATH_SIG_BYTES) + SCHNORR_SCRIPTPATH_SIG_BYTES + compactSizeBytes(leafScriptBytes) + leafScriptBytes + compactSizeBytes(controlBlockBytes) + controlBlockBytes;
|
|
1068
|
+
});
|
|
1069
|
+
return { scriptSig: 0, witness: Math.max(keyPathWitness, ...leafWitnesses) };
|
|
1070
|
+
}
|
|
1071
|
+
function computeInputCost(node) {
|
|
1072
|
+
switch (node.kind) {
|
|
1073
|
+
case "pk":
|
|
1074
|
+
case "pkh":
|
|
1075
|
+
case "wpkh":
|
|
1076
|
+
case "multi":
|
|
1077
|
+
return { ok: true, ...leafInputCost(node) };
|
|
1078
|
+
case "sh": {
|
|
1079
|
+
const { inner } = node;
|
|
1080
|
+
if (inner.kind === "wpkh") {
|
|
1081
|
+
const redeemBytes = simpleOutputScriptBytes(inner);
|
|
1082
|
+
return {
|
|
1083
|
+
ok: true,
|
|
1084
|
+
scriptSig: pushDataPrefixBytes(redeemBytes) + redeemBytes,
|
|
1085
|
+
witness: leafInputCost(inner).witness
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
if (inner.kind === "wsh") {
|
|
1089
|
+
const { inner: innerInner } = inner;
|
|
1090
|
+
if (innerInner.kind !== "pk" && innerInner.kind !== "pkh" && innerInner.kind !== "multi") {
|
|
1091
|
+
return weightFail(
|
|
1092
|
+
"unsupported",
|
|
1093
|
+
`sh(wsh(${innerInner.kind}(...))) is not a supported nesting`
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
const redeemBytes = P2WSH_SCRIPT_BYTES;
|
|
1097
|
+
return {
|
|
1098
|
+
ok: true,
|
|
1099
|
+
scriptSig: pushDataPrefixBytes(redeemBytes) + redeemBytes,
|
|
1100
|
+
witness: segwitWitnessBytes(innerInner)
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
if (inner.kind === "pk" || inner.kind === "pkh" || inner.kind === "multi") {
|
|
1104
|
+
const redeemBytes = simpleOutputScriptBytes(inner);
|
|
1105
|
+
return {
|
|
1106
|
+
ok: true,
|
|
1107
|
+
scriptSig: leafInputCost(inner).scriptSig + pushDataPrefixBytes(redeemBytes) + redeemBytes,
|
|
1108
|
+
witness: 0
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
return weightFail("unsupported", `sh(${inner.kind}(...)) is not a supported nesting`);
|
|
1112
|
+
}
|
|
1113
|
+
case "wsh": {
|
|
1114
|
+
const { inner } = node;
|
|
1115
|
+
if (inner.kind === "pk" || inner.kind === "pkh" || inner.kind === "multi") {
|
|
1116
|
+
return { ok: true, scriptSig: 0, witness: segwitWitnessBytes(inner) };
|
|
1117
|
+
}
|
|
1118
|
+
return weightFail("unsupported", `wsh(${inner.kind}(...)) is not a supported nesting`);
|
|
1119
|
+
}
|
|
1120
|
+
case "tr":
|
|
1121
|
+
return { ok: true, ...computeTrCost(node) };
|
|
1122
|
+
case "combo":
|
|
1123
|
+
return weightFail(
|
|
1124
|
+
"ambiguous-script-type",
|
|
1125
|
+
"combo() represents multiple candidate script types; describe the specific script you intend to spend instead"
|
|
1126
|
+
);
|
|
1127
|
+
case "raw":
|
|
1128
|
+
return weightFail(
|
|
1129
|
+
"opaque-script",
|
|
1130
|
+
"raw() has no known spending script, so its input weight cannot be determined"
|
|
1131
|
+
);
|
|
1132
|
+
case "addr":
|
|
1133
|
+
return weightFail(
|
|
1134
|
+
"opaque-script",
|
|
1135
|
+
"addr() has no known spending script, so its input weight cannot be determined"
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function decodeAddressScriptBytes(address, chain) {
|
|
1140
|
+
const segwit = bech32Decode(address);
|
|
1141
|
+
if (segwit !== void 0) {
|
|
1142
|
+
return { ok: true, value: 2 + segwit.program.length };
|
|
1143
|
+
}
|
|
1144
|
+
const decoded = base58Decode(address);
|
|
1145
|
+
if (decoded?.length === BASE58_ADDRESS_BYTES) {
|
|
1146
|
+
const version = decoded[0];
|
|
1147
|
+
if (version === chain.base58PubKeyHash) {
|
|
1148
|
+
return { ok: true, value: P2PKH_SCRIPT_BYTES };
|
|
1149
|
+
}
|
|
1150
|
+
if (version === chain.base58ScriptHash) {
|
|
1151
|
+
return { ok: true, value: P2SH_SCRIPT_BYTES };
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return weightFail("unsupported", `unrecognized address for chain '${chain.name}': ${address}`);
|
|
1155
|
+
}
|
|
1156
|
+
function computeOutputScriptBytes(node, options) {
|
|
1157
|
+
switch (node.kind) {
|
|
1158
|
+
case "pk":
|
|
1159
|
+
case "pkh":
|
|
1160
|
+
case "wpkh":
|
|
1161
|
+
case "sh":
|
|
1162
|
+
case "wsh":
|
|
1163
|
+
case "tr":
|
|
1164
|
+
case "multi":
|
|
1165
|
+
return { ok: true, value: simpleOutputScriptBytes(node) };
|
|
1166
|
+
case "combo":
|
|
1167
|
+
return weightFail(
|
|
1168
|
+
"ambiguous-script-type",
|
|
1169
|
+
"combo() represents multiple candidate scriptPubKeys; describe the specific script you intend to create instead"
|
|
1170
|
+
);
|
|
1171
|
+
case "raw":
|
|
1172
|
+
if (node.hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(node.hex)) {
|
|
1173
|
+
return weightFail("unsupported", "raw() requires an even-length hex string");
|
|
1174
|
+
}
|
|
1175
|
+
return { ok: true, value: node.hex.length / 2 };
|
|
1176
|
+
case "addr":
|
|
1177
|
+
return decodeAddressScriptBytes(node.address, options.chain ?? BITCOIN);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
function inputWeight(node) {
|
|
1181
|
+
const cost = computeInputCost(node);
|
|
1182
|
+
if (!cost.ok) {
|
|
1183
|
+
return cost;
|
|
1184
|
+
}
|
|
1185
|
+
const baseBytes = LEGACY_TX_OVERHEAD_BYTES + compactSizeBytes(cost.scriptSig) + cost.scriptSig;
|
|
1186
|
+
return { ok: true, weight: baseBytes * WEIGHT_SCALE + cost.witness };
|
|
1187
|
+
}
|
|
1188
|
+
function outputWeight(node, options = {}) {
|
|
1189
|
+
const scriptBytesResult = computeOutputScriptBytes(node, options);
|
|
1190
|
+
if (!scriptBytesResult.ok) {
|
|
1191
|
+
return scriptBytesResult;
|
|
1192
|
+
}
|
|
1193
|
+
const weight = (VOUT_OVERHEAD_BYTES + compactSizeBytes(scriptBytesResult.value) + scriptBytesResult.value) * WEIGHT_SCALE;
|
|
1194
|
+
return { ok: true, weight };
|
|
1195
|
+
}
|
|
1196
|
+
function describeInput(node) {
|
|
1197
|
+
const cost = computeInputCost(node);
|
|
1198
|
+
if (!cost.ok) {
|
|
1199
|
+
return cost;
|
|
1200
|
+
}
|
|
1201
|
+
if (node.kind === "pk") {
|
|
1202
|
+
return { ok: true, description: "p2pk" };
|
|
1203
|
+
}
|
|
1204
|
+
if (node.kind === "pkh") {
|
|
1205
|
+
return { ok: true, description: "p2pkh" };
|
|
1206
|
+
}
|
|
1207
|
+
if (node.kind === "wpkh") {
|
|
1208
|
+
return { ok: true, description: "p2wpkh" };
|
|
1209
|
+
}
|
|
1210
|
+
if (node.kind === "tr" && node.tree === void 0) {
|
|
1211
|
+
return { ok: true, description: "p2tr" };
|
|
1212
|
+
}
|
|
1213
|
+
if (node.kind === "sh" && node.inner.kind === "wpkh") {
|
|
1214
|
+
return { ok: true, description: "p2sh-p2wpkh" };
|
|
1215
|
+
}
|
|
1216
|
+
if (node.kind === "sh" && node.inner.kind === "multi") {
|
|
1217
|
+
return {
|
|
1218
|
+
ok: true,
|
|
1219
|
+
description: { type: "p2sh-multisig", m: node.inner.threshold, n: node.inner.keys.length }
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
if (node.kind === "wsh" && node.inner.kind === "multi") {
|
|
1223
|
+
return {
|
|
1224
|
+
ok: true,
|
|
1225
|
+
description: { type: "p2wsh-multisig", m: node.inner.threshold, n: node.inner.keys.length }
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
if (node.kind === "sh" && node.inner.kind === "wsh" && node.inner.inner.kind === "multi") {
|
|
1229
|
+
return {
|
|
1230
|
+
ok: true,
|
|
1231
|
+
description: {
|
|
1232
|
+
type: "p2sh-p2wsh-multisig",
|
|
1233
|
+
m: node.inner.inner.threshold,
|
|
1234
|
+
n: node.inner.inner.keys.length
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
return {
|
|
1239
|
+
ok: true,
|
|
1240
|
+
description: { type: "raw", scriptSigBytes: cost.scriptSig, witnessBytes: cost.witness }
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
function describeOutput(node, options = {}) {
|
|
1244
|
+
const scriptBytesResult = computeOutputScriptBytes(node, options);
|
|
1245
|
+
if (!scriptBytesResult.ok) {
|
|
1246
|
+
return scriptBytesResult;
|
|
1247
|
+
}
|
|
1248
|
+
const resolvedNode = node;
|
|
1249
|
+
switch (resolvedNode.kind) {
|
|
1250
|
+
case "pk":
|
|
1251
|
+
return { ok: true, description: "p2pk" };
|
|
1252
|
+
case "pkh":
|
|
1253
|
+
return { ok: true, description: "p2pkh" };
|
|
1254
|
+
case "wpkh":
|
|
1255
|
+
return { ok: true, description: "p2wpkh" };
|
|
1256
|
+
case "sh":
|
|
1257
|
+
return { ok: true, description: "p2sh" };
|
|
1258
|
+
case "wsh":
|
|
1259
|
+
return { ok: true, description: "p2wsh" };
|
|
1260
|
+
case "tr":
|
|
1261
|
+
return { ok: true, description: "p2tr" };
|
|
1262
|
+
case "multi":
|
|
1263
|
+
case "raw":
|
|
1264
|
+
case "addr":
|
|
1265
|
+
return { ok: true, description: { type: "raw", scriptPubKeyBytes: scriptBytesResult.value } };
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
exports.BITCOIN = BITCOIN;
|
|
1270
|
+
exports.BITCOIN_TESTNET = BITCOIN_TESTNET;
|
|
1271
|
+
exports.LITECOIN = LITECOIN;
|
|
1272
|
+
exports.LITECOIN_TESTNET = LITECOIN_TESTNET;
|
|
1273
|
+
exports.anyKeyRanged = anyKeyRanged;
|
|
1274
|
+
exports.checksumCreate = checksumCreate;
|
|
1275
|
+
exports.checksumVerify = checksumVerify;
|
|
1276
|
+
exports.collectTapLeaves = collectTapLeaves;
|
|
1277
|
+
exports.describeInput = describeInput;
|
|
1278
|
+
exports.describeOutput = describeOutput;
|
|
1279
|
+
exports.expandMultipath = expandMultipath;
|
|
1280
|
+
exports.forEachKey = forEachKey;
|
|
1281
|
+
exports.hasValidCharset = hasValidCharset;
|
|
1282
|
+
exports.inputWeight = inputWeight;
|
|
1283
|
+
exports.outputWeight = outputWeight;
|
|
1284
|
+
exports.parseDescriptor = parseDescriptor;
|
|
1285
|
+
exports.parseKeyExpression = parseKeyExpression;
|
|
1286
|
+
//# sourceMappingURL=index.cjs.map
|
|
1287
|
+
//# sourceMappingURL=index.cjs.map
|