@bitcoinerlab/descriptors 3.0.6 → 3.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/README.md +9 -487
- package/index.d.ts +13 -0
- package/index.js +16 -0
- package/package.json +22 -53
- package/dist/applyPR2137.d.ts +0 -2
- package/dist/applyPR2137.js +0 -153
- package/dist/bitcoinjs-lib-internals.d.ts +0 -10
- package/dist/bitcoinjs-lib-internals.js +0 -60
- package/dist/checksum.d.ts +0 -6
- package/dist/checksum.js +0 -58
- package/dist/descriptors.d.ts +0 -433
- package/dist/descriptors.js +0 -1743
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -85
- package/dist/keyExpressions.d.ts +0 -83
- package/dist/keyExpressions.js +0 -247
- package/dist/ledger.d.ts +0 -167
- package/dist/ledger.js +0 -580
- package/dist/miniscript.d.ts +0 -123
- package/dist/miniscript.js +0 -305
- package/dist/multipath.d.ts +0 -13
- package/dist/multipath.js +0 -76
- package/dist/networkUtils.d.ts +0 -3
- package/dist/networkUtils.js +0 -16
- package/dist/parseUtils.d.ts +0 -7
- package/dist/parseUtils.js +0 -46
- package/dist/psbt.d.ts +0 -44
- package/dist/psbt.js +0 -193
- package/dist/re.d.ts +0 -31
- package/dist/re.js +0 -79
- package/dist/resourceLimits.d.ts +0 -25
- package/dist/resourceLimits.js +0 -89
- package/dist/scriptExpressions.d.ts +0 -95
- package/dist/scriptExpressions.js +0 -89
- package/dist/signers.d.ts +0 -84
- package/dist/signers.js +0 -215
- package/dist/stackResourceLimits.d.ts +0 -17
- package/dist/stackResourceLimits.js +0 -35
- package/dist/tapMiniscript.d.ts +0 -220
- package/dist/tapMiniscript.js +0 -510
- package/dist/tapTree.d.ts +0 -86
- package/dist/tapTree.js +0 -166
- package/dist/types.d.ts +0 -238
- package/dist/types.js +0 -4
package/dist/descriptors.js
DELETED
|
@@ -1,1743 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright (c) 2026 Jose-Luis Landabaso - https://bitcoinerlab.com
|
|
3
|
-
// Distributed under the MIT software license
|
|
4
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
-
if (k2 === undefined) k2 = k;
|
|
6
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
-
}
|
|
10
|
-
Object.defineProperty(o, k2, desc);
|
|
11
|
-
}) : (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
o[k2] = m[k];
|
|
14
|
-
}));
|
|
15
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
-
}) : function(o, v) {
|
|
18
|
-
o["default"] = v;
|
|
19
|
-
});
|
|
20
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
-
var ownKeys = function(o) {
|
|
22
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
-
var ar = [];
|
|
24
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
-
return ar;
|
|
26
|
-
};
|
|
27
|
-
return ownKeys(o);
|
|
28
|
-
};
|
|
29
|
-
return function (mod) {
|
|
30
|
-
if (mod && mod.__esModule) return mod;
|
|
31
|
-
var result = {};
|
|
32
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
-
__setModuleDefault(result, mod);
|
|
34
|
-
return result;
|
|
35
|
-
};
|
|
36
|
-
})();
|
|
37
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
38
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
39
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
40
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
41
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
42
|
-
};
|
|
43
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
44
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
45
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
46
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
47
|
-
};
|
|
48
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
49
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
50
|
-
};
|
|
51
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
-
exports.DescriptorsFactory = DescriptorsFactory;
|
|
53
|
-
const lodash_memoize_1 = __importDefault(require("lodash.memoize")); //TODO: make sure this is propoely used
|
|
54
|
-
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
55
|
-
const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
|
|
56
|
-
const varuint_bitcoin_1 = require("varuint-bitcoin");
|
|
57
|
-
const uint8array_tools_1 = require("uint8array-tools");
|
|
58
|
-
const { p2sh, p2wpkh, p2pkh, p2pk, p2wsh, p2tr } = bitcoinjs_lib_1.payments;
|
|
59
|
-
const bip32_1 = require("bip32");
|
|
60
|
-
const ecpair_1 = require("ecpair");
|
|
61
|
-
const psbt_1 = require("./psbt");
|
|
62
|
-
const checksum_1 = require("./checksum");
|
|
63
|
-
const keyExpressions_1 = require("./keyExpressions");
|
|
64
|
-
const RE = __importStar(require("./re"));
|
|
65
|
-
const miniscript_1 = require("./miniscript");
|
|
66
|
-
const tapTree_1 = require("./tapTree");
|
|
67
|
-
const tapMiniscript_1 = require("./tapMiniscript");
|
|
68
|
-
const parseUtils_1 = require("./parseUtils");
|
|
69
|
-
const multipath_1 = require("./multipath");
|
|
70
|
-
const resourceLimits_1 = require("./resourceLimits");
|
|
71
|
-
const ECDSA_FAKE_SIGNATURE_SIZE = 72;
|
|
72
|
-
const TAPROOT_FAKE_SIGNATURE_SIZE = 64;
|
|
73
|
-
function vectorSize(someVector) {
|
|
74
|
-
const length = someVector.length;
|
|
75
|
-
return ((0, varuint_bitcoin_1.encodingLength)(length) +
|
|
76
|
-
someVector.reduce((sum, witness) => {
|
|
77
|
-
return sum + varSliceSize(witness);
|
|
78
|
-
}, 0));
|
|
79
|
-
}
|
|
80
|
-
function varSliceSize(someScript) {
|
|
81
|
-
const length = someScript.length;
|
|
82
|
-
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
83
|
-
}
|
|
84
|
-
/*
|
|
85
|
-
* Returns a bare descriptor without checksum and particularized for a certain
|
|
86
|
-
* index (if desc was a range descriptor)
|
|
87
|
-
* @hidden
|
|
88
|
-
*/
|
|
89
|
-
function evaluate({ descriptor, checksumRequired, index, change }) {
|
|
90
|
-
if (!descriptor)
|
|
91
|
-
throw new Error('You must provide a descriptor.');
|
|
92
|
-
const mChecksum = descriptor.match(String.raw `(${RE.reChecksum})$`);
|
|
93
|
-
if (mChecksum === null && checksumRequired === true)
|
|
94
|
-
throw new Error(`Error: descriptor ${descriptor} has not checksum`);
|
|
95
|
-
//evaluatedDescriptor: a bare desc without checksum and particularized for a certain
|
|
96
|
-
//index (if desc was a range descriptor)
|
|
97
|
-
let evaluatedDescriptor = descriptor;
|
|
98
|
-
if (mChecksum !== null) {
|
|
99
|
-
const checksum = mChecksum[0].substring(1); //remove the leading #
|
|
100
|
-
evaluatedDescriptor = descriptor.substring(0, descriptor.length - mChecksum[0].length);
|
|
101
|
-
if (checksum !== (0, checksum_1.DescriptorChecksum)(evaluatedDescriptor)) {
|
|
102
|
-
throw new Error(`Error: invalid descriptor checksum for ${descriptor}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
evaluatedDescriptor = (0, multipath_1.resolveMultipathDescriptor)({
|
|
106
|
-
descriptor: evaluatedDescriptor,
|
|
107
|
-
...(change !== undefined ? { change } : {})
|
|
108
|
-
});
|
|
109
|
-
if (index !== undefined) {
|
|
110
|
-
const mWildcard = evaluatedDescriptor.match(/\*/g);
|
|
111
|
-
if (mWildcard && mWildcard.length > 0) {
|
|
112
|
-
//From https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
|
|
113
|
-
//To prevent a combinatorial explosion of the search space, if more than
|
|
114
|
-
//one of the multi() key arguments is a BIP32 wildcard path ending in /* or
|
|
115
|
-
//*', the multi() descriptor only matches multisig scripts with the ith
|
|
116
|
-
//child key from each wildcard path in lockstep, rather than scripts with
|
|
117
|
-
//any combination of child keys from each wildcard path.
|
|
118
|
-
//We extend this reasoning for musig for all cases
|
|
119
|
-
evaluatedDescriptor = evaluatedDescriptor.replaceAll('*', index.toString());
|
|
120
|
-
}
|
|
121
|
-
else
|
|
122
|
-
throw new Error(`Error: index passed for non-ranged descriptor: ${descriptor}`);
|
|
123
|
-
}
|
|
124
|
-
return evaluatedDescriptor;
|
|
125
|
-
}
|
|
126
|
-
// Helper: parse sortedmulti(M, k1, k2,...)
|
|
127
|
-
function parseSortedMulti(inner) {
|
|
128
|
-
// inner: "2,key1,key2,key3"
|
|
129
|
-
const parts = inner.split(',').map(p => p.trim());
|
|
130
|
-
if (parts.length < 2)
|
|
131
|
-
throw new Error(`sortedmulti(): must contain M and at least one key: ${inner}`);
|
|
132
|
-
const m = Number(parts[0]);
|
|
133
|
-
if (!Number.isInteger(m) || m < 1 || m > 20)
|
|
134
|
-
throw new Error(`sortedmulti(): invalid M=${parts[0]}`);
|
|
135
|
-
const keyExpressions = parts.slice(1);
|
|
136
|
-
if (keyExpressions.length < m)
|
|
137
|
-
throw new Error(`sortedmulti(): M cannot exceed number of keys: ${inner}`);
|
|
138
|
-
if (keyExpressions.length > 20)
|
|
139
|
-
throw new Error(`sortedmulti(): descriptors support up to 20 keys (per BIP 380/383).`);
|
|
140
|
-
return { m, keyExpressions };
|
|
141
|
-
}
|
|
142
|
-
const MAX_PUBKEYS_PER_MULTI_A = 999;
|
|
143
|
-
// Helper: parse sortedmulti_a(M, k1, k2,...)
|
|
144
|
-
function parseSortedMultiA(inner) {
|
|
145
|
-
const parts = inner.split(',').map(p => p.trim());
|
|
146
|
-
if (parts.length < 2)
|
|
147
|
-
throw new Error(`sortedmulti_a(): must contain M and at least one key: ${inner}`);
|
|
148
|
-
const m = Number(parts[0]);
|
|
149
|
-
if (!Number.isInteger(m) || m < 1)
|
|
150
|
-
throw new Error(`sortedmulti_a(): invalid M=${parts[0]}`);
|
|
151
|
-
const keyExpressions = parts.slice(1);
|
|
152
|
-
if (keyExpressions.length < m)
|
|
153
|
-
throw new Error(`sortedmulti_a(): M cannot exceed number of keys: ${inner}`);
|
|
154
|
-
if (keyExpressions.length > MAX_PUBKEYS_PER_MULTI_A)
|
|
155
|
-
throw new Error(`sortedmulti_a(): descriptors support up to ${MAX_PUBKEYS_PER_MULTI_A} keys.`);
|
|
156
|
-
return { m, keyExpressions };
|
|
157
|
-
}
|
|
158
|
-
function parseTrExpression(expression) {
|
|
159
|
-
if (!expression.startsWith('tr(') || !expression.endsWith(')'))
|
|
160
|
-
throw new Error(`Error: invalid descriptor ${expression}`);
|
|
161
|
-
const innerExpression = expression.slice(3, -1).trim();
|
|
162
|
-
if (!innerExpression)
|
|
163
|
-
throw new Error(`Error: invalid descriptor ${expression}`);
|
|
164
|
-
const splitResult = (0, parseUtils_1.splitTopLevelComma)({
|
|
165
|
-
expression: innerExpression,
|
|
166
|
-
onError: () => new Error(`Error: invalid descriptor ${expression}`)
|
|
167
|
-
});
|
|
168
|
-
//if no commas: innerExpression === keyExpression
|
|
169
|
-
if (!splitResult)
|
|
170
|
-
return { keyExpression: innerExpression };
|
|
171
|
-
return { keyExpression: splitResult.left, treeExpression: splitResult.right };
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Constructs the necessary functions and classes for working with descriptors
|
|
175
|
-
* using an external elliptic curve (ecc) library.
|
|
176
|
-
*
|
|
177
|
-
* Notably, it returns the {@link _Internal_.Output | `Output`} class, which
|
|
178
|
-
* provides methods to create, sign, and finalize PSBTs based on descriptor
|
|
179
|
-
* expressions.
|
|
180
|
-
*
|
|
181
|
-
* The Factory also returns utility methods like `expand` (detailed below)
|
|
182
|
-
* and `parseKeyExpression` (see {@link KeyExpressionParser}).
|
|
183
|
-
*
|
|
184
|
-
* Additionally, for convenience, the function returns `BIP32` and `ECPair`.
|
|
185
|
-
* These are {@link https://github.com/bitcoinjs bitcoinjs-lib} classes designed
|
|
186
|
-
* for managing {@link https://github.com/bitcoinjs/bip32 | `BIP32`} keys and
|
|
187
|
-
* public/private key pairs:
|
|
188
|
-
* {@link https://github.com/bitcoinjs/ecpair | `ECPair`}, respectively.
|
|
189
|
-
*
|
|
190
|
-
* @param {Object} ecc - An object with elliptic curve operations, such as
|
|
191
|
-
* [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) or
|
|
192
|
-
* [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1).
|
|
193
|
-
*/
|
|
194
|
-
function DescriptorsFactory(ecc) {
|
|
195
|
-
var _Output_instances, _Output_payment, _Output_preimages, _Output_signersPubKeys, _Output_miniscript, _Output_witnessScript, _Output_redeemScript, _Output_isSegwit, _Output_isTaproot, _Output_expandedExpression, _Output_expandedMiniscript, _Output_tapTreeExpression, _Output_tapTree, _Output_tapTreeInfo, _Output_taprootSpendPath, _Output_tapLeaf, _Output_expansionMap, _Output_network, _Output_resolveMiniscriptSignersPubKeys, _Output_assertMiniscriptSatisfactionResourceLimits, _Output_resolveTapTreeSignersPubKeys, _Output_getConstraints, _Output_assertPsbtInput;
|
|
196
|
-
(0, bitcoinjs_lib_1.initEccLib)(ecc); //Taproot requires initEccLib
|
|
197
|
-
const BIP32 = (0, bip32_1.BIP32Factory)(ecc);
|
|
198
|
-
const ECPair = (0, ecpair_1.ECPairFactory)(ecc);
|
|
199
|
-
const signatureValidator = (pubkey, msghash, signature) => {
|
|
200
|
-
if (pubkey.length === 32) {
|
|
201
|
-
//x-only
|
|
202
|
-
if (!ecc.verifySchnorr) {
|
|
203
|
-
throw new Error('TinySecp256k1Interface is not initialized properly: verifySchnorr is missing.');
|
|
204
|
-
}
|
|
205
|
-
return ecc.verifySchnorr(msghash, pubkey, signature);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
return ECPair.fromPublicKey(pubkey).verify(msghash, signature);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
/**
|
|
212
|
-
* Takes a string key expression (xpub, xprv, pubkey or wif) and parses it
|
|
213
|
-
*/
|
|
214
|
-
const parseKeyExpression = ({ keyExpression, isSegwit, isTaproot, network = bitcoinjs_lib_1.networks.bitcoin }) => {
|
|
215
|
-
return (0, keyExpressions_1.parseKeyExpression)({
|
|
216
|
-
keyExpression,
|
|
217
|
-
network,
|
|
218
|
-
...(typeof isSegwit === 'boolean' ? { isSegwit } : {}),
|
|
219
|
-
...(typeof isTaproot === 'boolean' ? { isTaproot } : {}),
|
|
220
|
-
ECPair,
|
|
221
|
-
BIP32
|
|
222
|
-
});
|
|
223
|
-
};
|
|
224
|
-
/**
|
|
225
|
-
* Builds a taproot leaf expansion override for descriptor-level
|
|
226
|
-
* `sortedmulti_a(...)`.
|
|
227
|
-
*
|
|
228
|
-
* Why this exists:
|
|
229
|
-
* - `sortedmulti_a` is a descriptor script expression (not a Miniscript
|
|
230
|
-
* fragment).
|
|
231
|
-
*
|
|
232
|
-
* What this does:
|
|
233
|
-
* - Resolves each key expression to a concrete pubkey and builds a leaf-local
|
|
234
|
-
* placeholder map (`@0`, `@1`, ... in input order).
|
|
235
|
-
* - Derives the internal compilation form by sorting placeholders by pubkey
|
|
236
|
-
* bytes and lowering to `multi_a(...)`.
|
|
237
|
-
* - Compiles tapscript from that lowered form and returns it as override
|
|
238
|
-
* data.
|
|
239
|
-
*
|
|
240
|
-
* Returns `undefined` for non-`sortedmulti_a` leaves so normal taproot miniscript
|
|
241
|
-
* expansion/compilation is used.
|
|
242
|
-
*/
|
|
243
|
-
function buildTapLeafSortedMultiAOverride({ expression, network = bitcoinjs_lib_1.networks.bitcoin }) {
|
|
244
|
-
if (!/\bsortedmulti_a\(/.test(expression))
|
|
245
|
-
return undefined;
|
|
246
|
-
const trimmed = expression.trim();
|
|
247
|
-
const match = trimmed.match(/^sortedmulti_a\((.*)\)$/);
|
|
248
|
-
if (!match)
|
|
249
|
-
throw new Error(`Error: sortedmulti_a() must be a standalone taproot leaf expression`);
|
|
250
|
-
const inner = match[1];
|
|
251
|
-
if (!inner)
|
|
252
|
-
throw new Error(`Error: invalid sortedmulti_a() expression: ${expression}`);
|
|
253
|
-
const { m, keyExpressions } = parseSortedMultiA(inner);
|
|
254
|
-
const keyInfos = keyExpressions.map(keyExpression => {
|
|
255
|
-
const keyInfo = parseKeyExpression({
|
|
256
|
-
keyExpression,
|
|
257
|
-
isSegwit: true,
|
|
258
|
-
isTaproot: true,
|
|
259
|
-
network
|
|
260
|
-
});
|
|
261
|
-
if (!keyInfo.pubkey)
|
|
262
|
-
throw new Error(`Error: sortedmulti_a() key must resolve to a concrete pubkey: ${keyExpression}`);
|
|
263
|
-
return keyInfo;
|
|
264
|
-
});
|
|
265
|
-
const expansionMap = {};
|
|
266
|
-
keyInfos.forEach((keyInfo, index) => {
|
|
267
|
-
expansionMap[`@${index}`] = keyInfo;
|
|
268
|
-
});
|
|
269
|
-
// sortedmulti_a is descriptor-level sugar. We preserve it in
|
|
270
|
-
// expandedExpression, but compile tapscript from its internal multi_a
|
|
271
|
-
// lowering with sorted placeholders.
|
|
272
|
-
const expandedExpression = `sortedmulti_a(${[
|
|
273
|
-
m,
|
|
274
|
-
...Object.keys(expansionMap)
|
|
275
|
-
].join(',')})`;
|
|
276
|
-
const compileExpandedMiniscript = (0, tapMiniscript_1.compileSortedMultiAExpandedExpression)({
|
|
277
|
-
expandedExpression,
|
|
278
|
-
expansionMap
|
|
279
|
-
});
|
|
280
|
-
const tapScript = (0, miniscript_1.miniscript2Script)({
|
|
281
|
-
expandedMiniscript: compileExpandedMiniscript,
|
|
282
|
-
expansionMap,
|
|
283
|
-
tapscript: true
|
|
284
|
-
});
|
|
285
|
-
return { expandedExpression, expansionMap, tapScript };
|
|
286
|
-
}
|
|
287
|
-
function expand({ descriptor, index, change, checksumRequired = false, network = bitcoinjs_lib_1.networks.bitcoin, allowMiniscriptInP2SH = false }) {
|
|
288
|
-
if (!descriptor)
|
|
289
|
-
throw new Error(`descriptor not provided`);
|
|
290
|
-
let expandedExpression;
|
|
291
|
-
let miniscript;
|
|
292
|
-
let expansionMap;
|
|
293
|
-
let isSegwit;
|
|
294
|
-
let isTaproot;
|
|
295
|
-
let expandedMiniscript;
|
|
296
|
-
let tapTreeExpression;
|
|
297
|
-
let tapTree;
|
|
298
|
-
let tapTreeInfo;
|
|
299
|
-
let payment;
|
|
300
|
-
let witnessScript;
|
|
301
|
-
let redeemScript;
|
|
302
|
-
const isRanged = descriptor.indexOf('*') !== -1;
|
|
303
|
-
if (index !== undefined)
|
|
304
|
-
if (!Number.isInteger(index) || index < 0)
|
|
305
|
-
throw new Error(`Error: invalid index ${index}`);
|
|
306
|
-
//Verify and remove checksum (if exists) and
|
|
307
|
-
//particularize range descriptor for index (if desc is range descriptor)
|
|
308
|
-
const canonicalExpression = evaluate({
|
|
309
|
-
descriptor,
|
|
310
|
-
...(index !== undefined ? { index } : {}),
|
|
311
|
-
...(change !== undefined ? { change } : {}),
|
|
312
|
-
checksumRequired
|
|
313
|
-
});
|
|
314
|
-
const isCanonicalRanged = canonicalExpression.indexOf('*') !== -1;
|
|
315
|
-
//addr(ADDR)
|
|
316
|
-
if (canonicalExpression.match(RE.reAddrAnchored)) {
|
|
317
|
-
if (isRanged)
|
|
318
|
-
throw new Error(`Error: addr() cannot be ranged`);
|
|
319
|
-
const matchedAddress = canonicalExpression.match(RE.reAddrAnchored)?.[1]; //[1]-> whatever is found addr(->HERE<-)
|
|
320
|
-
if (!matchedAddress)
|
|
321
|
-
throw new Error(`Error: could not get an address in ${descriptor}`);
|
|
322
|
-
let output;
|
|
323
|
-
try {
|
|
324
|
-
output = bitcoinjs_lib_1.address.toOutputScript(matchedAddress, network);
|
|
325
|
-
}
|
|
326
|
-
catch (e) {
|
|
327
|
-
void e;
|
|
328
|
-
throw new Error(`Error: invalid address ${matchedAddress}`);
|
|
329
|
-
}
|
|
330
|
-
try {
|
|
331
|
-
payment = p2pkh({ output, network });
|
|
332
|
-
isSegwit = false;
|
|
333
|
-
isTaproot = false;
|
|
334
|
-
}
|
|
335
|
-
catch (e) {
|
|
336
|
-
void e;
|
|
337
|
-
}
|
|
338
|
-
try {
|
|
339
|
-
payment = p2sh({ output, network });
|
|
340
|
-
// It assumes that an addr(SH_ADDRESS) is always a add(SH_WPKH) address
|
|
341
|
-
isSegwit = true;
|
|
342
|
-
isTaproot = false;
|
|
343
|
-
}
|
|
344
|
-
catch (e) {
|
|
345
|
-
void e;
|
|
346
|
-
}
|
|
347
|
-
try {
|
|
348
|
-
payment = p2wpkh({ output, network });
|
|
349
|
-
isSegwit = true;
|
|
350
|
-
isTaproot = false;
|
|
351
|
-
}
|
|
352
|
-
catch (e) {
|
|
353
|
-
void e;
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
payment = p2wsh({ output, network });
|
|
357
|
-
isSegwit = true;
|
|
358
|
-
isTaproot = false;
|
|
359
|
-
}
|
|
360
|
-
catch (e) {
|
|
361
|
-
void e;
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
payment = p2tr({ output, network });
|
|
365
|
-
isSegwit = true;
|
|
366
|
-
isTaproot = true;
|
|
367
|
-
}
|
|
368
|
-
catch (e) {
|
|
369
|
-
void e;
|
|
370
|
-
}
|
|
371
|
-
if (!payment) {
|
|
372
|
-
throw new Error(`Error: invalid address ${matchedAddress}`);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
//pk(KEY)
|
|
376
|
-
else if (canonicalExpression.match(RE.rePkAnchored)) {
|
|
377
|
-
isSegwit = false;
|
|
378
|
-
isTaproot = false;
|
|
379
|
-
const keyExpression = canonicalExpression.match(RE.reNonSegwitKeyExp)?.[0];
|
|
380
|
-
if (!keyExpression)
|
|
381
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
382
|
-
if (canonicalExpression !== `pk(${keyExpression})`)
|
|
383
|
-
throw new Error(`Error: invalid expression ${descriptor}`);
|
|
384
|
-
expandedExpression = 'pk(@0)';
|
|
385
|
-
const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
|
|
386
|
-
expansionMap = { '@0': pKE };
|
|
387
|
-
if (!isCanonicalRanged) {
|
|
388
|
-
const pubkey = pKE.pubkey;
|
|
389
|
-
//Note there exists no address for p2pk, but we can still use the script
|
|
390
|
-
if (!pubkey)
|
|
391
|
-
throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
|
|
392
|
-
payment = p2pk({ pubkey, network });
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
//pkh(KEY) - legacy
|
|
396
|
-
else if (canonicalExpression.match(RE.rePkhAnchored)) {
|
|
397
|
-
isSegwit = false;
|
|
398
|
-
isTaproot = false;
|
|
399
|
-
const keyExpression = canonicalExpression.match(RE.reNonSegwitKeyExp)?.[0];
|
|
400
|
-
if (!keyExpression)
|
|
401
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
402
|
-
if (canonicalExpression !== `pkh(${keyExpression})`)
|
|
403
|
-
throw new Error(`Error: invalid expression ${descriptor}`);
|
|
404
|
-
expandedExpression = 'pkh(@0)';
|
|
405
|
-
const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
|
|
406
|
-
expansionMap = { '@0': pKE };
|
|
407
|
-
if (!isCanonicalRanged) {
|
|
408
|
-
const pubkey = pKE.pubkey;
|
|
409
|
-
if (!pubkey)
|
|
410
|
-
throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
|
|
411
|
-
payment = p2pkh({ pubkey, network });
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
//sh(wpkh(KEY)) - nested segwit
|
|
415
|
-
else if (canonicalExpression.match(RE.reShWpkhAnchored)) {
|
|
416
|
-
isSegwit = true;
|
|
417
|
-
isTaproot = false;
|
|
418
|
-
const keyExpression = canonicalExpression.match(RE.reSegwitKeyExp)?.[0];
|
|
419
|
-
if (!keyExpression)
|
|
420
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
421
|
-
if (canonicalExpression !== `sh(wpkh(${keyExpression}))`)
|
|
422
|
-
throw new Error(`Error: invalid expression ${descriptor}`);
|
|
423
|
-
expandedExpression = 'sh(wpkh(@0))';
|
|
424
|
-
const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
|
|
425
|
-
expansionMap = { '@0': pKE };
|
|
426
|
-
if (!isCanonicalRanged) {
|
|
427
|
-
const pubkey = pKE.pubkey;
|
|
428
|
-
if (!pubkey)
|
|
429
|
-
throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
|
|
430
|
-
payment = p2sh({ redeem: p2wpkh({ pubkey, network }), network });
|
|
431
|
-
redeemScript = payment.redeem?.output;
|
|
432
|
-
if (!redeemScript)
|
|
433
|
-
throw new Error(`Error: could not calculate redeemScript for ${descriptor}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
//wpkh(KEY) - native segwit
|
|
437
|
-
else if (canonicalExpression.match(RE.reWpkhAnchored)) {
|
|
438
|
-
isSegwit = true;
|
|
439
|
-
isTaproot = false;
|
|
440
|
-
const keyExpression = canonicalExpression.match(RE.reSegwitKeyExp)?.[0];
|
|
441
|
-
if (!keyExpression)
|
|
442
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
443
|
-
if (canonicalExpression !== `wpkh(${keyExpression})`)
|
|
444
|
-
throw new Error(`Error: invalid expression ${descriptor}`);
|
|
445
|
-
expandedExpression = 'wpkh(@0)';
|
|
446
|
-
const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
|
|
447
|
-
expansionMap = { '@0': pKE };
|
|
448
|
-
if (!isCanonicalRanged) {
|
|
449
|
-
const pubkey = pKE.pubkey;
|
|
450
|
-
if (!pubkey)
|
|
451
|
-
throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
|
|
452
|
-
payment = p2wpkh({ pubkey, network });
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
// sortedmulti script expressions
|
|
456
|
-
// sh(sortedmulti())
|
|
457
|
-
else if (canonicalExpression.match(RE.reShSortedMultiAnchored)) {
|
|
458
|
-
isSegwit = false;
|
|
459
|
-
isTaproot = false;
|
|
460
|
-
const inner = canonicalExpression.match(RE.reShSortedMultiAnchored)?.[1];
|
|
461
|
-
if (!inner)
|
|
462
|
-
throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
|
|
463
|
-
const { m, keyExpressions } = parseSortedMulti(inner);
|
|
464
|
-
const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: false }));
|
|
465
|
-
const map = {};
|
|
466
|
-
pKEs.forEach((pke, i) => (map['@' + i] = pke));
|
|
467
|
-
expansionMap = map;
|
|
468
|
-
expandedExpression =
|
|
469
|
-
'sh(sortedmulti(' +
|
|
470
|
-
[m, ...Object.keys(expansionMap).map(k => k)].join(',') +
|
|
471
|
-
'))';
|
|
472
|
-
if (!isCanonicalRanged) {
|
|
473
|
-
const pubkeys = pKEs.map(p => {
|
|
474
|
-
if (!p.pubkey)
|
|
475
|
-
throw new Error(`Error: key has no pubkey`);
|
|
476
|
-
return p.pubkey;
|
|
477
|
-
});
|
|
478
|
-
pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
|
|
479
|
-
const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
|
|
480
|
-
redeemScript = redeem.output;
|
|
481
|
-
if (!redeemScript)
|
|
482
|
-
throw new Error(`Error creating redeemScript`);
|
|
483
|
-
payment = bitcoinjs_lib_1.payments.p2sh({ redeem, network });
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// wsh(sortedmulti())
|
|
487
|
-
else if (canonicalExpression.match(RE.reWshSortedMultiAnchored)) {
|
|
488
|
-
isSegwit = true;
|
|
489
|
-
isTaproot = false;
|
|
490
|
-
const inner = canonicalExpression.match(RE.reWshSortedMultiAnchored)?.[1];
|
|
491
|
-
if (!inner)
|
|
492
|
-
throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
|
|
493
|
-
const { m, keyExpressions } = parseSortedMulti(inner);
|
|
494
|
-
const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: true }));
|
|
495
|
-
const map = {};
|
|
496
|
-
pKEs.forEach((pke, i) => (map['@' + i] = pke));
|
|
497
|
-
expansionMap = map;
|
|
498
|
-
expandedExpression =
|
|
499
|
-
'wsh(sortedmulti(' +
|
|
500
|
-
[m, ...Object.keys(expansionMap).map(k => k)].join(',') +
|
|
501
|
-
'))';
|
|
502
|
-
if (!isCanonicalRanged) {
|
|
503
|
-
const pubkeys = pKEs.map(p => {
|
|
504
|
-
if (!p.pubkey)
|
|
505
|
-
throw new Error(`Error: key has no pubkey`);
|
|
506
|
-
return p.pubkey;
|
|
507
|
-
});
|
|
508
|
-
pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
|
|
509
|
-
const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
|
|
510
|
-
witnessScript = redeem.output;
|
|
511
|
-
if (!witnessScript)
|
|
512
|
-
throw new Error(`Error computing witnessScript`);
|
|
513
|
-
payment = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
// sh(wsh(sortedmulti()))
|
|
517
|
-
else if (canonicalExpression.match(RE.reShWshSortedMultiAnchored)) {
|
|
518
|
-
isSegwit = true;
|
|
519
|
-
isTaproot = false;
|
|
520
|
-
const inner = canonicalExpression.match(RE.reShWshSortedMultiAnchored)?.[1];
|
|
521
|
-
if (!inner)
|
|
522
|
-
throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
|
|
523
|
-
const { m, keyExpressions } = parseSortedMulti(inner);
|
|
524
|
-
const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: true }));
|
|
525
|
-
const map = {};
|
|
526
|
-
pKEs.forEach((pke, i) => (map['@' + i] = pke));
|
|
527
|
-
expansionMap = map;
|
|
528
|
-
expandedExpression =
|
|
529
|
-
'sh(wsh(sortedmulti(' +
|
|
530
|
-
[m, ...Object.keys(expansionMap).map(k => k)].join(',') +
|
|
531
|
-
')))';
|
|
532
|
-
if (!isCanonicalRanged) {
|
|
533
|
-
const pubkeys = pKEs.map(p => {
|
|
534
|
-
if (!p.pubkey)
|
|
535
|
-
throw new Error(`Error: key has no pubkey`);
|
|
536
|
-
return p.pubkey;
|
|
537
|
-
});
|
|
538
|
-
pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
|
|
539
|
-
const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
|
|
540
|
-
const wsh = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
|
|
541
|
-
witnessScript = redeem.output;
|
|
542
|
-
redeemScript = wsh.output;
|
|
543
|
-
payment = bitcoinjs_lib_1.payments.p2sh({ redeem: wsh, network });
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
//sh(wsh(miniscript))
|
|
547
|
-
else if (canonicalExpression.match(RE.reShWshMiniscriptAnchored)) {
|
|
548
|
-
isSegwit = true;
|
|
549
|
-
isTaproot = false;
|
|
550
|
-
miniscript = canonicalExpression.match(RE.reShWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(wsh(->HERE<-))
|
|
551
|
-
if (!miniscript)
|
|
552
|
-
throw new Error(`Error: could not get miniscript in ${descriptor}`);
|
|
553
|
-
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
554
|
-
miniscript,
|
|
555
|
-
isSegwit,
|
|
556
|
-
network
|
|
557
|
-
}));
|
|
558
|
-
expandedExpression = `sh(wsh(${expandedMiniscript}))`;
|
|
559
|
-
if (!isCanonicalRanged) {
|
|
560
|
-
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
561
|
-
witnessScript = script;
|
|
562
|
-
if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
563
|
-
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
564
|
-
}
|
|
565
|
-
(0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
|
|
566
|
-
payment = p2sh({
|
|
567
|
-
redeem: p2wsh({ redeem: { output: script, network }, network }),
|
|
568
|
-
network
|
|
569
|
-
});
|
|
570
|
-
redeemScript = payment.redeem?.output;
|
|
571
|
-
if (!redeemScript)
|
|
572
|
-
throw new Error(`Error: could not calculate redeemScript for ${descriptor}`);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
//sh(miniscript)
|
|
576
|
-
else if (canonicalExpression.match(RE.reShMiniscriptAnchored)) {
|
|
577
|
-
//isSegwit false because we know it's a P2SH of a miniscript and not a
|
|
578
|
-
//P2SH that embeds a witness payment.
|
|
579
|
-
isSegwit = false;
|
|
580
|
-
isTaproot = false;
|
|
581
|
-
miniscript = canonicalExpression.match(RE.reShMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(->HERE<-)
|
|
582
|
-
if (!miniscript)
|
|
583
|
-
throw new Error(`Error: could not get miniscript in ${descriptor}`);
|
|
584
|
-
if (allowMiniscriptInP2SH === false &&
|
|
585
|
-
// These top-level script expressions are allowed inside sh(...).
|
|
586
|
-
// The sorted script expressions (`sortedmulti`, `sortedmulti_a`) are
|
|
587
|
-
// handled in dedicated descriptor/taproot branches and are intentionally
|
|
588
|
-
// not part of this miniscript gate.
|
|
589
|
-
// Here we only keep legacy top-level forms to avoid accepting arbitrary
|
|
590
|
-
// miniscript in P2SH unless explicitly enabled.
|
|
591
|
-
miniscript.search(/^(pk\(|pkh\(|wpkh\(|combo\(|multi\(|multi_a\()/) !==
|
|
592
|
-
0) {
|
|
593
|
-
throw new Error(`Error: Miniscript expressions can only be used in wsh`);
|
|
594
|
-
}
|
|
595
|
-
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
596
|
-
miniscript,
|
|
597
|
-
isSegwit,
|
|
598
|
-
network
|
|
599
|
-
}));
|
|
600
|
-
expandedExpression = `sh(${expandedMiniscript})`;
|
|
601
|
-
if (!isCanonicalRanged) {
|
|
602
|
-
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
603
|
-
redeemScript = script;
|
|
604
|
-
if (script.byteLength > resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE) {
|
|
605
|
-
throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE} bytes`);
|
|
606
|
-
}
|
|
607
|
-
(0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
|
|
608
|
-
payment = p2sh({ redeem: { output: script, network }, network });
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
//wsh(miniscript)
|
|
612
|
-
else if (canonicalExpression.match(RE.reWshMiniscriptAnchored)) {
|
|
613
|
-
isSegwit = true;
|
|
614
|
-
isTaproot = false;
|
|
615
|
-
miniscript = canonicalExpression.match(RE.reWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found wsh(->HERE<-)
|
|
616
|
-
if (!miniscript)
|
|
617
|
-
throw new Error(`Error: could not get miniscript in ${descriptor}`);
|
|
618
|
-
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
619
|
-
miniscript,
|
|
620
|
-
isSegwit,
|
|
621
|
-
network
|
|
622
|
-
}));
|
|
623
|
-
expandedExpression = `wsh(${expandedMiniscript})`;
|
|
624
|
-
if (!isCanonicalRanged) {
|
|
625
|
-
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
626
|
-
witnessScript = script;
|
|
627
|
-
if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
628
|
-
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
629
|
-
}
|
|
630
|
-
(0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
|
|
631
|
-
payment = p2wsh({ redeem: { output: script, network }, network });
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
//tr(KEY) or tr(KEY,TREE) - taproot
|
|
635
|
-
else if (canonicalExpression.startsWith('tr(')) {
|
|
636
|
-
isSegwit = true;
|
|
637
|
-
isTaproot = true;
|
|
638
|
-
const { keyExpression, treeExpression } = parseTrExpression(canonicalExpression);
|
|
639
|
-
expandedExpression = treeExpression
|
|
640
|
-
? `tr(@0,${treeExpression})`
|
|
641
|
-
: 'tr(@0)';
|
|
642
|
-
const pKE = parseKeyExpression({
|
|
643
|
-
keyExpression,
|
|
644
|
-
network,
|
|
645
|
-
isSegwit,
|
|
646
|
-
isTaproot
|
|
647
|
-
});
|
|
648
|
-
expansionMap = { '@0': pKE };
|
|
649
|
-
if (treeExpression) {
|
|
650
|
-
tapTreeExpression = treeExpression;
|
|
651
|
-
tapTree = (0, tapTree_1.parseTapTreeExpression)(treeExpression);
|
|
652
|
-
if (!isCanonicalRanged) {
|
|
653
|
-
tapTreeInfo = (0, tapMiniscript_1.buildTapTreeInfo)({
|
|
654
|
-
tapTree,
|
|
655
|
-
network,
|
|
656
|
-
BIP32,
|
|
657
|
-
ECPair,
|
|
658
|
-
// `leafExpansionOverride` runs per leaf expression.
|
|
659
|
-
// For non-matching leaves it returns undefined and
|
|
660
|
-
// normal miniscript expansion is used;
|
|
661
|
-
// for sortedmulti_a leaves it returns descriptor-level
|
|
662
|
-
// metadata plus precompiled tapscript bytes.
|
|
663
|
-
leafExpansionOverride: (expression) => buildTapLeafSortedMultiAOverride({ expression, network })
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
if (!isCanonicalRanged) {
|
|
668
|
-
const pubkey = pKE.pubkey;
|
|
669
|
-
if (!pubkey)
|
|
670
|
-
throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
|
|
671
|
-
const internalPubkey = (0, tapMiniscript_1.normalizeTaprootPubkey)(pubkey);
|
|
672
|
-
if (treeExpression) {
|
|
673
|
-
if (!tapTreeInfo)
|
|
674
|
-
throw new Error(`Error: taproot tree info not available`);
|
|
675
|
-
payment = p2tr({
|
|
676
|
-
internalPubkey,
|
|
677
|
-
scriptTree: (0, tapMiniscript_1.tapTreeInfoToScriptTree)(tapTreeInfo),
|
|
678
|
-
network
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
else {
|
|
682
|
-
payment = p2tr({ internalPubkey, network });
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
else {
|
|
687
|
-
throw new Error(`Error: Could not parse descriptor ${descriptor}`);
|
|
688
|
-
}
|
|
689
|
-
return {
|
|
690
|
-
...(payment !== undefined ? { payment } : {}),
|
|
691
|
-
...(expandedExpression !== undefined ? { expandedExpression } : {}),
|
|
692
|
-
...(miniscript !== undefined ? { miniscript } : {}),
|
|
693
|
-
...(expansionMap !== undefined ? { expansionMap } : {}),
|
|
694
|
-
...(isSegwit !== undefined ? { isSegwit } : {}),
|
|
695
|
-
...(isTaproot !== undefined ? { isTaproot } : {}),
|
|
696
|
-
...(expandedMiniscript !== undefined ? { expandedMiniscript } : {}),
|
|
697
|
-
...(tapTreeExpression !== undefined ? { tapTreeExpression } : {}),
|
|
698
|
-
...(tapTree !== undefined ? { tapTree } : {}),
|
|
699
|
-
...(tapTreeInfo !== undefined ? { tapTreeInfo } : {}),
|
|
700
|
-
...(redeemScript !== undefined ? { redeemScript } : {}),
|
|
701
|
-
...(witnessScript !== undefined ? { witnessScript } : {}),
|
|
702
|
-
isRanged,
|
|
703
|
-
canonicalExpression
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Expand a miniscript to a generalized form using variables instead of key
|
|
708
|
-
* expressions. Variables will be of this form: @0, @1, ...
|
|
709
|
-
* This is done so that it can be compiled with compileMiniscript and
|
|
710
|
-
* satisfied with satisfier.
|
|
711
|
-
* Also compute pubkeys from descriptors to use them later.
|
|
712
|
-
*/
|
|
713
|
-
function expandMiniscript({ miniscript, isSegwit, network = bitcoinjs_lib_1.networks.bitcoin }) {
|
|
714
|
-
return (0, miniscript_1.expandMiniscript)({
|
|
715
|
-
miniscript,
|
|
716
|
-
isSegwit,
|
|
717
|
-
isTaproot: false, //TODO:
|
|
718
|
-
network,
|
|
719
|
-
BIP32,
|
|
720
|
-
ECPair
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* The `Output` class is the central component for managing descriptors.
|
|
725
|
-
* It facilitates the creation of outputs to receive funds and enables the
|
|
726
|
-
* signing and finalization of PSBTs (Partially Signed Bitcoin Transactions)
|
|
727
|
-
* for spending UTXOs (Unspent Transaction Outputs).
|
|
728
|
-
*/
|
|
729
|
-
class Output {
|
|
730
|
-
/**
|
|
731
|
-
* @param options
|
|
732
|
-
* @throws {Error} - when descriptor is invalid
|
|
733
|
-
*/
|
|
734
|
-
constructor({ descriptor, index, change, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys, taprootSpendPath, tapLeaf }) {
|
|
735
|
-
_Output_instances.add(this);
|
|
736
|
-
_Output_payment.set(this, void 0);
|
|
737
|
-
_Output_preimages.set(this, []);
|
|
738
|
-
_Output_signersPubKeys.set(this, void 0);
|
|
739
|
-
_Output_miniscript.set(this, void 0);
|
|
740
|
-
_Output_witnessScript.set(this, void 0);
|
|
741
|
-
_Output_redeemScript.set(this, void 0);
|
|
742
|
-
//isSegwit true if witnesses are needed to the spend coins sent to this descriptor.
|
|
743
|
-
//may be unset because we may get addr(P2SH) which we don't know if they have segwit.
|
|
744
|
-
_Output_isSegwit.set(this, void 0);
|
|
745
|
-
_Output_isTaproot.set(this, void 0);
|
|
746
|
-
_Output_expandedExpression.set(this, void 0);
|
|
747
|
-
_Output_expandedMiniscript.set(this, void 0);
|
|
748
|
-
_Output_tapTreeExpression.set(this, void 0);
|
|
749
|
-
_Output_tapTree.set(this, void 0);
|
|
750
|
-
_Output_tapTreeInfo.set(this, void 0);
|
|
751
|
-
_Output_taprootSpendPath.set(this, void 0);
|
|
752
|
-
_Output_tapLeaf.set(this, void 0);
|
|
753
|
-
_Output_expansionMap.set(this, void 0);
|
|
754
|
-
_Output_network.set(this, void 0);
|
|
755
|
-
__classPrivateFieldSet(this, _Output_network, network, "f");
|
|
756
|
-
__classPrivateFieldSet(this, _Output_preimages, preimages, "f");
|
|
757
|
-
if (typeof descriptor !== 'string')
|
|
758
|
-
throw new Error(`Error: invalid descriptor type`);
|
|
759
|
-
const expandedResult = expand({
|
|
760
|
-
descriptor,
|
|
761
|
-
...(index !== undefined ? { index } : {}),
|
|
762
|
-
...(change !== undefined ? { change } : {}),
|
|
763
|
-
checksumRequired,
|
|
764
|
-
network,
|
|
765
|
-
allowMiniscriptInP2SH
|
|
766
|
-
});
|
|
767
|
-
const isTaprootDescriptor = expandedResult.isTaproot === true;
|
|
768
|
-
const hasTapTree = expandedResult.expandedExpression?.startsWith('tr(@0,') ?? false;
|
|
769
|
-
const resolvedTaprootSpendPath = taprootSpendPath ?? (hasTapTree ? 'script' : 'key');
|
|
770
|
-
if (!isTaprootDescriptor) {
|
|
771
|
-
if (taprootSpendPath !== undefined || tapLeaf !== undefined)
|
|
772
|
-
throw new Error(`Error: taprootSpendPath/tapLeaf require a taproot descriptor`);
|
|
773
|
-
}
|
|
774
|
-
else {
|
|
775
|
-
if (taprootSpendPath === 'script' && !hasTapTree)
|
|
776
|
-
throw new Error(`Error: taprootSpendPath=script requires a tr(KEY,TREE) descriptor`);
|
|
777
|
-
if (resolvedTaprootSpendPath === 'key' && tapLeaf !== undefined)
|
|
778
|
-
throw new Error(`Error: tapLeaf cannot be used when taprootSpendPath is key`);
|
|
779
|
-
if (tapLeaf !== undefined && !hasTapTree)
|
|
780
|
-
throw new Error(`Error: tapLeaf can only be used with tr(KEY,TREE) descriptors`);
|
|
781
|
-
}
|
|
782
|
-
if (expandedResult.isRanged && index === undefined)
|
|
783
|
-
throw new Error(`Error: index was not provided for ranged descriptor`);
|
|
784
|
-
if (!expandedResult.payment)
|
|
785
|
-
throw new Error(`Error: could not extract a payment from ${descriptor}`);
|
|
786
|
-
__classPrivateFieldSet(this, _Output_payment, expandedResult.payment, "f");
|
|
787
|
-
if (expandedResult.expandedExpression !== undefined)
|
|
788
|
-
__classPrivateFieldSet(this, _Output_expandedExpression, expandedResult.expandedExpression, "f");
|
|
789
|
-
if (expandedResult.miniscript !== undefined)
|
|
790
|
-
__classPrivateFieldSet(this, _Output_miniscript, expandedResult.miniscript, "f");
|
|
791
|
-
if (expandedResult.expansionMap !== undefined)
|
|
792
|
-
__classPrivateFieldSet(this, _Output_expansionMap, expandedResult.expansionMap, "f");
|
|
793
|
-
if (expandedResult.isSegwit !== undefined)
|
|
794
|
-
__classPrivateFieldSet(this, _Output_isSegwit, expandedResult.isSegwit, "f");
|
|
795
|
-
if (expandedResult.isTaproot !== undefined)
|
|
796
|
-
__classPrivateFieldSet(this, _Output_isTaproot, expandedResult.isTaproot, "f");
|
|
797
|
-
if (expandedResult.expandedMiniscript !== undefined)
|
|
798
|
-
__classPrivateFieldSet(this, _Output_expandedMiniscript, expandedResult.expandedMiniscript, "f");
|
|
799
|
-
if (expandedResult.tapTreeExpression !== undefined)
|
|
800
|
-
__classPrivateFieldSet(this, _Output_tapTreeExpression, expandedResult.tapTreeExpression, "f");
|
|
801
|
-
if (expandedResult.tapTree !== undefined)
|
|
802
|
-
__classPrivateFieldSet(this, _Output_tapTree, expandedResult.tapTree, "f");
|
|
803
|
-
if (expandedResult.tapTreeInfo !== undefined)
|
|
804
|
-
__classPrivateFieldSet(this, _Output_tapTreeInfo, expandedResult.tapTreeInfo, "f");
|
|
805
|
-
if (expandedResult.redeemScript !== undefined)
|
|
806
|
-
__classPrivateFieldSet(this, _Output_redeemScript, expandedResult.redeemScript, "f");
|
|
807
|
-
if (expandedResult.witnessScript !== undefined)
|
|
808
|
-
__classPrivateFieldSet(this, _Output_witnessScript, expandedResult.witnessScript, "f");
|
|
809
|
-
if (signersPubKeys)
|
|
810
|
-
__classPrivateFieldSet(this, _Output_signersPubKeys, signersPubKeys, "f");
|
|
811
|
-
__classPrivateFieldSet(this, _Output_taprootSpendPath, resolvedTaprootSpendPath, "f");
|
|
812
|
-
if (tapLeaf !== undefined)
|
|
813
|
-
__classPrivateFieldSet(this, _Output_tapLeaf, tapLeaf, "f");
|
|
814
|
-
this.getSequence = (0, lodash_memoize_1.default)(this.getSequence);
|
|
815
|
-
this.getLockTime = (0, lodash_memoize_1.default)(this.getLockTime);
|
|
816
|
-
const getSignaturesKey = (signatures) => signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
|
|
817
|
-
? signatures
|
|
818
|
-
: signatures
|
|
819
|
-
.map(s => `${(0, uint8array_tools_1.toHex)(s.pubkey)}-${(0, uint8array_tools_1.toHex)(s.signature)}`)
|
|
820
|
-
.join('|');
|
|
821
|
-
this.guessOutput = (0, lodash_memoize_1.default)(this.guessOutput);
|
|
822
|
-
this.inputWeight = (0, lodash_memoize_1.default)(this.inputWeight,
|
|
823
|
-
// resolver function:
|
|
824
|
-
(isSegwitTx, signatures, options) => {
|
|
825
|
-
const segwitKey = isSegwitTx ? 'segwit' : 'non-segwit';
|
|
826
|
-
const signaturesKey = getSignaturesKey(signatures);
|
|
827
|
-
const taprootSighashKey = options?.taprootSighash ?? 'SIGHASH_DEFAULT';
|
|
828
|
-
return `${segwitKey}-${signaturesKey}-taprootSighash:${taprootSighashKey}`;
|
|
829
|
-
});
|
|
830
|
-
this.outputWeight = (0, lodash_memoize_1.default)(this.outputWeight);
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Returns the compiled Script Satisfaction for a miniscript-based Output.
|
|
834
|
-
* The satisfaction is the unlocking script, derived by the Satisfier
|
|
835
|
-
* algorithm (https://bitcoin.sipa.be/miniscript/).
|
|
836
|
-
*
|
|
837
|
-
* This method uses a two-pass flow:
|
|
838
|
-
* 1) Planning: constraints (nLockTime/nSequence) are computed using fake
|
|
839
|
-
* signatures. This is done since the final solution may not need all the
|
|
840
|
-
* signatures in signersPubKeys. And we may avoid the user do extra
|
|
841
|
-
* signing (tedious op with HWW).
|
|
842
|
-
* 2) Signing: the provided signatures are used to build the final
|
|
843
|
-
* satisfaction, while enforcing the planned constraints so the same
|
|
844
|
-
* solution is selected. Not all the signatures of signersPubKeys may
|
|
845
|
-
* be required.
|
|
846
|
-
*
|
|
847
|
-
* The return value includes the satisfaction script and the constraints.
|
|
848
|
-
*/
|
|
849
|
-
getScriptSatisfaction(
|
|
850
|
-
/**
|
|
851
|
-
* An array with all the signatures needed to
|
|
852
|
-
* build the Satisfaction of this miniscript-based `Output`.
|
|
853
|
-
*
|
|
854
|
-
* `signatures` must be passed using this format (pairs of `pubKey/signature`):
|
|
855
|
-
* `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
|
|
856
|
-
*/
|
|
857
|
-
signatures) {
|
|
858
|
-
const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
|
|
859
|
-
const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
|
|
860
|
-
const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
|
|
861
|
-
if (miniscript === undefined ||
|
|
862
|
-
expandedMiniscript === undefined ||
|
|
863
|
-
expansionMap === undefined)
|
|
864
|
-
throw new Error(`Error: cannot get satisfaction from not expanded miniscript ${miniscript}`);
|
|
865
|
-
//This crates the plans using fake signatures
|
|
866
|
-
const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
|
|
867
|
-
const satisfaction = (0, miniscript_1.satisfyMiniscript)({
|
|
868
|
-
expandedMiniscript,
|
|
869
|
-
expansionMap,
|
|
870
|
-
signatures,
|
|
871
|
-
preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
|
|
872
|
-
//Here we pass the TimeConstraints obtained using signersPubKeys to
|
|
873
|
-
//verify that the solutions found using the final signatures have not
|
|
874
|
-
//changed
|
|
875
|
-
timeConstraints: {
|
|
876
|
-
nLockTime: constraints?.nLockTime,
|
|
877
|
-
nSequence: constraints?.nSequence
|
|
878
|
-
}
|
|
879
|
-
});
|
|
880
|
-
__classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
|
|
881
|
-
return satisfaction;
|
|
882
|
-
}
|
|
883
|
-
/**
|
|
884
|
-
* Returns the taproot script‑path satisfaction for a tap miniscript
|
|
885
|
-
* descriptor. This mirrors {@link getScriptSatisfaction} and uses the same
|
|
886
|
-
* two‑pass plan/sign flow.
|
|
887
|
-
*
|
|
888
|
-
* In addition to nLockTime/nSequence, it returns the selected tapLeafHash
|
|
889
|
-
* (the leaf chosen during planning) and the leaf’s tapscript.
|
|
890
|
-
*/
|
|
891
|
-
getTapScriptSatisfaction(
|
|
892
|
-
/**
|
|
893
|
-
* An array with all the signatures needed to
|
|
894
|
-
* build the Satisfaction of this miniscript-based `Output`.
|
|
895
|
-
*
|
|
896
|
-
* `signatures` must be passed using this format (pairs of `pubKey/signature`):
|
|
897
|
-
* `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
|
|
898
|
-
*/
|
|
899
|
-
signatures) {
|
|
900
|
-
if (__classPrivateFieldGet(this, _Output_taprootSpendPath, "f") !== 'script')
|
|
901
|
-
throw new Error(`Error: taprootSpendPath is key; script-path satisfaction is not allowed`);
|
|
902
|
-
const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
|
|
903
|
-
if (!tapTreeInfo)
|
|
904
|
-
throw new Error(`Error: taproot tree info not available`);
|
|
905
|
-
const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
|
|
906
|
-
return (0, tapMiniscript_1.satisfyTapTree)({
|
|
907
|
-
tapTreeInfo,
|
|
908
|
-
preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
|
|
909
|
-
signatures,
|
|
910
|
-
...(constraints?.tapLeafHash
|
|
911
|
-
? { tapLeaf: constraints.tapLeafHash }
|
|
912
|
-
: {}),
|
|
913
|
-
...(constraints
|
|
914
|
-
? {
|
|
915
|
-
timeConstraints: {
|
|
916
|
-
nLockTime: constraints.nLockTime,
|
|
917
|
-
nSequence: constraints.nSequence
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
: {})
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
/**
|
|
924
|
-
* Creates and returns an instance of bitcoinjs-lib
|
|
925
|
-
* [`Payment`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts)'s interface with the `scriptPubKey` of this `Output`.
|
|
926
|
-
*/
|
|
927
|
-
getPayment() {
|
|
928
|
-
return __classPrivateFieldGet(this, _Output_payment, "f");
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* Returns the Bitcoin Address of this `Output`.
|
|
932
|
-
*/
|
|
933
|
-
getAddress() {
|
|
934
|
-
if (!__classPrivateFieldGet(this, _Output_payment, "f").address)
|
|
935
|
-
throw new Error(`Error: could extract an address from the payment`);
|
|
936
|
-
return __classPrivateFieldGet(this, _Output_payment, "f").address;
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Returns this `Output`'s scriptPubKey.
|
|
940
|
-
*/
|
|
941
|
-
getScriptPubKey() {
|
|
942
|
-
if (!__classPrivateFieldGet(this, _Output_payment, "f").output)
|
|
943
|
-
throw new Error(`Error: could extract output.script from the payment`);
|
|
944
|
-
return __classPrivateFieldGet(this, _Output_payment, "f").output;
|
|
945
|
-
}
|
|
946
|
-
/**
|
|
947
|
-
* Gets the nSequence required to fulfill this `Output`.
|
|
948
|
-
*/
|
|
949
|
-
getSequence() {
|
|
950
|
-
return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nSequence;
|
|
951
|
-
}
|
|
952
|
-
/**
|
|
953
|
-
* Gets the nLockTime required to fulfill this `Output`.
|
|
954
|
-
*/
|
|
955
|
-
getLockTime() {
|
|
956
|
-
return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nLockTime;
|
|
957
|
-
}
|
|
958
|
-
/**
|
|
959
|
-
* Returns the tapleaf hash selected during planning for taproot script-path
|
|
960
|
-
* spends. If signersPubKeys are provided, selection is optimized for those
|
|
961
|
-
* pubkeys. If a specific tapLeaf selector is used in spending calls, this
|
|
962
|
-
* reflects that selection.
|
|
963
|
-
*/
|
|
964
|
-
getTapLeafHash() {
|
|
965
|
-
return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.tapLeafHash;
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Gets the witnessScript required to fulfill this `Output`. Only applies to
|
|
969
|
-
* Segwit outputs.
|
|
970
|
-
*/
|
|
971
|
-
getWitnessScript() {
|
|
972
|
-
return __classPrivateFieldGet(this, _Output_witnessScript, "f");
|
|
973
|
-
}
|
|
974
|
-
/**
|
|
975
|
-
* Gets the redeemScript required to fullfill this `Output`. Only applies to
|
|
976
|
-
* SH outputs: sh(wpkh), sh(wsh), sh(lockingScript).
|
|
977
|
-
*/
|
|
978
|
-
getRedeemScript() {
|
|
979
|
-
return __classPrivateFieldGet(this, _Output_redeemScript, "f");
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Gets the bitcoinjs-lib [`network`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/networks.ts) used to create this `Output`.
|
|
983
|
-
*/
|
|
984
|
-
getNetwork() {
|
|
985
|
-
return __classPrivateFieldGet(this, _Output_network, "f");
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Whether this `Output` is Segwit.
|
|
989
|
-
*
|
|
990
|
-
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
991
|
-
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
992
|
-
* (Script Hash-Witness Public Key Hash).
|
|
993
|
-
* For inputs using arbitrary scripts (not standard addresses),
|
|
994
|
-
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
995
|
-
*
|
|
996
|
-
*/
|
|
997
|
-
isSegwit() {
|
|
998
|
-
return __classPrivateFieldGet(this, _Output_isSegwit, "f");
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Whether this `Output` is Taproot.
|
|
1002
|
-
*/
|
|
1003
|
-
isTaproot() {
|
|
1004
|
-
return __classPrivateFieldGet(this, _Output_isTaproot, "f");
|
|
1005
|
-
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Attempts to determine the type of output script by testing it against
|
|
1008
|
-
* various payment types.
|
|
1009
|
-
*
|
|
1010
|
-
* This method tries to identify if the output is one of the following types:
|
|
1011
|
-
* - P2SH (Pay to Script Hash)
|
|
1012
|
-
* - P2WSH (Pay to Witness Script Hash)
|
|
1013
|
-
* - P2WPKH (Pay to Witness Public Key Hash)
|
|
1014
|
-
* - P2PKH (Pay to Public Key Hash)
|
|
1015
|
-
* - P2TR (Pay to Taproot)
|
|
1016
|
-
*
|
|
1017
|
-
* @returns An object { isPKH: boolean; isWPKH: boolean; isSH: boolean; isWSH: boolean; isTR: boolean;}
|
|
1018
|
-
* with boolean properties indicating the detected output type
|
|
1019
|
-
*/
|
|
1020
|
-
guessOutput() {
|
|
1021
|
-
function guessSH(output) {
|
|
1022
|
-
try {
|
|
1023
|
-
bitcoinjs_lib_1.payments.p2sh({ output });
|
|
1024
|
-
return true;
|
|
1025
|
-
}
|
|
1026
|
-
catch (err) {
|
|
1027
|
-
void err;
|
|
1028
|
-
return false;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
function guessWSH(output) {
|
|
1032
|
-
try {
|
|
1033
|
-
bitcoinjs_lib_1.payments.p2wsh({ output });
|
|
1034
|
-
return true;
|
|
1035
|
-
}
|
|
1036
|
-
catch (err) {
|
|
1037
|
-
void err;
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
function guessWPKH(output) {
|
|
1042
|
-
try {
|
|
1043
|
-
bitcoinjs_lib_1.payments.p2wpkh({ output });
|
|
1044
|
-
return true;
|
|
1045
|
-
}
|
|
1046
|
-
catch (err) {
|
|
1047
|
-
void err;
|
|
1048
|
-
return false;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
function guessPKH(output) {
|
|
1052
|
-
try {
|
|
1053
|
-
bitcoinjs_lib_1.payments.p2pkh({ output });
|
|
1054
|
-
return true;
|
|
1055
|
-
}
|
|
1056
|
-
catch (err) {
|
|
1057
|
-
void err;
|
|
1058
|
-
return false;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
function guessTR(output) {
|
|
1062
|
-
try {
|
|
1063
|
-
bitcoinjs_lib_1.payments.p2tr({ output });
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
catch (err) {
|
|
1067
|
-
void err;
|
|
1068
|
-
return false;
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
const isPKH = guessPKH(this.getScriptPubKey());
|
|
1072
|
-
const isWPKH = guessWPKH(this.getScriptPubKey());
|
|
1073
|
-
const isSH = guessSH(this.getScriptPubKey());
|
|
1074
|
-
const isWSH = guessWSH(this.getScriptPubKey());
|
|
1075
|
-
const isTR = guessTR(this.getScriptPubKey());
|
|
1076
|
-
if ([isPKH, isWPKH, isSH, isWSH, isTR].filter(Boolean).length > 1)
|
|
1077
|
-
throw new Error('Cannot have multiple output types.');
|
|
1078
|
-
return { isPKH, isWPKH, isSH, isWSH, isTR };
|
|
1079
|
-
}
|
|
1080
|
-
// References for inputWeight & outputWeight:
|
|
1081
|
-
// https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c
|
|
1082
|
-
// https://bitcoinops.org/en/tools/calc-size/
|
|
1083
|
-
// Look for byteLength: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/transaction.ts
|
|
1084
|
-
// https://github.com/bitcoinjs/coinselect/blob/master/utils.js
|
|
1085
|
-
// https://bitcoin.stackexchange.com/questions/111395/what-is-the-weight-of-a-p2tr-input
|
|
1086
|
-
/**
|
|
1087
|
-
* Computes the Weight Unit contributions of this Output as if it were the
|
|
1088
|
-
* input in a tx.
|
|
1089
|
-
*
|
|
1090
|
-
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
1091
|
-
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
1092
|
-
* (Script Hash-Witness Public Key Hash).
|
|
1093
|
-
*, Also any `addr(SINGLE_KEY_ADDRESS)` * is assumed to be a single key Taproot
|
|
1094
|
-
* address (like those defined in BIP86).
|
|
1095
|
-
* For inputs using arbitrary scripts (not standard addresses),
|
|
1096
|
-
* use a descriptor in the format `sh(MINISCRIPT)`, `wsh(MINISCRIPT)` or
|
|
1097
|
-
* `tr(KEY,TREE)` for taproot script-path expressions.
|
|
1098
|
-
*/
|
|
1099
|
-
// NOTE(taproot-weight): Output instances are concrete. If descriptor has
|
|
1100
|
-
// wildcards, constructor requires `index`. No ranged-without-index
|
|
1101
|
-
// estimation is attempted here.
|
|
1102
|
-
// TODO(taproot-weight): Remaining items:
|
|
1103
|
-
// - Annex: not modeled; if annex is used, add witness item sizing.
|
|
1104
|
-
// - Taproot sighash defaults: options.taprootSighash currently drives fake
|
|
1105
|
-
// signature sizing; ensure coinselector passes the intended mode.
|
|
1106
|
-
// - After PSBT taproot script-path fields are fully populated, add regtest
|
|
1107
|
-
// integration fixtures comparing real tx vsize with inputWeight/outputWeight
|
|
1108
|
-
// estimates for taproot key-path and script-path spends.
|
|
1109
|
-
inputWeight(
|
|
1110
|
-
/**
|
|
1111
|
-
* Indicates if the transaction is a Segwit transaction.
|
|
1112
|
-
* If a transaction isSegwitTx, a single byte is then also required for
|
|
1113
|
-
* non-witness inputs to encode the length of the empty witness stack:
|
|
1114
|
-
* encodeLength(0) + 0 = 1
|
|
1115
|
-
* Read more:
|
|
1116
|
-
* https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c?permalink_comment_id=4760512#gistcomment-4760512
|
|
1117
|
-
*/
|
|
1118
|
-
isSegwitTx,
|
|
1119
|
-
/*
|
|
1120
|
-
* Array of `PartialSig`. Each `PartialSig` includes
|
|
1121
|
-
* a public key and its corresponding signature. This parameter
|
|
1122
|
-
* enables the accurate calculation of signature sizes for ECDSA.
|
|
1123
|
-
* Pass 'DANGEROUSLY_USE_FAKE_SIGNATURES' to assume
|
|
1124
|
-
* ECDSA_FAKE_SIGNATURE_SIZE bytes for ECDSA.
|
|
1125
|
-
* For taproot, the fake signature size is controlled by
|
|
1126
|
-
* options.taprootSighash (64 for 'SIGHASH_DEFAULT', 65
|
|
1127
|
-
* for 'non-SIGHASH_DEFAULT'). default value is SIGHASH_DEFAULT
|
|
1128
|
-
* Mainly used for testing.
|
|
1129
|
-
*/
|
|
1130
|
-
signatures,
|
|
1131
|
-
/*
|
|
1132
|
-
* Options that affect taproot fake signature sizing.
|
|
1133
|
-
* taprootSighash: 'SIGHASH_DEFAULT' | 'non-SIGHASH_DEFAULT' (default: 'SIGHASH_DEFAULT').
|
|
1134
|
-
* This is only used when signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'.
|
|
1135
|
-
*/
|
|
1136
|
-
options = {
|
|
1137
|
-
taprootSighash: 'SIGHASH_DEFAULT'
|
|
1138
|
-
}) {
|
|
1139
|
-
const taprootSighash = options.taprootSighash ?? 'SIGHASH_DEFAULT';
|
|
1140
|
-
if (this.isSegwit() && !isSegwitTx)
|
|
1141
|
-
throw new Error(`a tx is segwit if at least one input is segwit`);
|
|
1142
|
-
//expand any miniscript-based descriptor. If not miniscript-based, then it's
|
|
1143
|
-
//an addr() descriptor. For those, we can only guess their type.
|
|
1144
|
-
const expansion = this.expand().expandedExpression;
|
|
1145
|
-
const { isPKH, isWPKH, isSH, isTR } = this.guessOutput();
|
|
1146
|
-
const errorMsg = `Input type not implemented. Currently supported: pkh(KEY), wpkh(KEY), tr(KEY), \
|
|
1147
|
-
sh(wpkh(KEY)), sh(wsh(MINISCRIPT)), sh(MINISCRIPT), wsh(MINISCRIPT), \
|
|
1148
|
-
addr(PKH_ADDRESS), addr(WPKH_ADDRESS), addr(SH_WPKH_ADDRESS), addr(SINGLE_KEY_ADDRESS). \
|
|
1149
|
-
expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${isTR}.`;
|
|
1150
|
-
if (!expansion && !isPKH && !isWPKH && !isSH && !isTR)
|
|
1151
|
-
throw new Error(errorMsg);
|
|
1152
|
-
const resolveEcdsaSignatureSize = () => {
|
|
1153
|
-
if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES')
|
|
1154
|
-
return ((0, varuint_bitcoin_1.encodingLength)(ECDSA_FAKE_SIGNATURE_SIZE) +
|
|
1155
|
-
ECDSA_FAKE_SIGNATURE_SIZE);
|
|
1156
|
-
if (signatures.length !== 1)
|
|
1157
|
-
throw new Error('More than one signture was not expected');
|
|
1158
|
-
const singleSignature = signatures[0];
|
|
1159
|
-
if (!singleSignature)
|
|
1160
|
-
throw new Error('Signatures not present');
|
|
1161
|
-
const length = singleSignature.signature.length;
|
|
1162
|
-
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
1163
|
-
};
|
|
1164
|
-
const resolveMiniscriptSignatures = () => {
|
|
1165
|
-
const signerPubKeys = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this);
|
|
1166
|
-
if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
|
|
1167
|
-
return signerPubKeys.map(pubkey => ({
|
|
1168
|
-
pubkey,
|
|
1169
|
-
// https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
|
|
1170
|
-
signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
|
|
1171
|
-
}));
|
|
1172
|
-
}
|
|
1173
|
-
const providedSignerPubKeysSet = new Set(signatures.map(sig => (0, uint8array_tools_1.toHex)(sig.pubkey)));
|
|
1174
|
-
const missingSignerPubKeys = signerPubKeys.filter(pubkey => !providedSignerPubKeysSet.has((0, uint8array_tools_1.toHex)(pubkey)));
|
|
1175
|
-
if (missingSignerPubKeys.length > 0)
|
|
1176
|
-
throw new Error(`Error: inputWeight expected signatures for all planned miniscript signers. Missing ${missingSignerPubKeys.length} signer(s)`);
|
|
1177
|
-
const signerPubKeysSet = new Set(signerPubKeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)));
|
|
1178
|
-
return signatures.filter(sig => signerPubKeysSet.has((0, uint8array_tools_1.toHex)(sig.pubkey)));
|
|
1179
|
-
};
|
|
1180
|
-
const taprootFakeSignatureSize = taprootSighash === 'SIGHASH_DEFAULT'
|
|
1181
|
-
? TAPROOT_FAKE_SIGNATURE_SIZE
|
|
1182
|
-
: TAPROOT_FAKE_SIGNATURE_SIZE + 1;
|
|
1183
|
-
const resolvePlannedTaprootRequiredPubKeys = () => {
|
|
1184
|
-
const tapTreeSignerPubKeys = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(tapMiniscript_1.normalizeTaprootPubkey);
|
|
1185
|
-
const taggedFakeSignatures = tapTreeSignerPubKeys.map((pubkey, index) => ({
|
|
1186
|
-
pubkey,
|
|
1187
|
-
signature: new Uint8Array(taprootFakeSignatureSize).fill(index + 1)
|
|
1188
|
-
}));
|
|
1189
|
-
const plannedTaprootSatisfaction = this.getTapScriptSatisfaction(taggedFakeSignatures);
|
|
1190
|
-
const requiredPubkeys = taggedFakeSignatures
|
|
1191
|
-
.filter(fakeSignature => plannedTaprootSatisfaction.stackItems.some(stackItem => (0, uint8array_tools_1.compare)(stackItem, fakeSignature.signature) === 0))
|
|
1192
|
-
.map(fakeSignature => fakeSignature.pubkey);
|
|
1193
|
-
return Array.from(new Set(requiredPubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey))))
|
|
1194
|
-
.map(hex => (0, uint8array_tools_1.fromHex)(hex))
|
|
1195
|
-
.map(tapMiniscript_1.normalizeTaprootPubkey);
|
|
1196
|
-
};
|
|
1197
|
-
const resolveTaprootSignatures = () => {
|
|
1198
|
-
if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
|
|
1199
|
-
return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
|
|
1200
|
-
pubkey,
|
|
1201
|
-
signature: new Uint8Array(taprootFakeSignatureSize)
|
|
1202
|
-
}));
|
|
1203
|
-
}
|
|
1204
|
-
const normalizedSignatures = signatures.map(sig => ({
|
|
1205
|
-
pubkey: (0, tapMiniscript_1.normalizeTaprootPubkey)(sig.pubkey),
|
|
1206
|
-
signature: sig.signature
|
|
1207
|
-
}));
|
|
1208
|
-
const plannedRequiredPubKeys = resolvePlannedTaprootRequiredPubKeys();
|
|
1209
|
-
const providedTapTreeSignerPubKeysSet = new Set(normalizedSignatures.map(sig => (0, uint8array_tools_1.toHex)(sig.pubkey)));
|
|
1210
|
-
const missingTapTreeSignerPubKeys = plannedRequiredPubKeys.filter(pubkey => !providedTapTreeSignerPubKeysSet.has((0, uint8array_tools_1.toHex)(pubkey)));
|
|
1211
|
-
if (missingTapTreeSignerPubKeys.length > 0)
|
|
1212
|
-
throw new Error(`Error: inputWeight expected signatures for the planned taproot script-path satisfaction. Missing ${missingTapTreeSignerPubKeys.length} signer(s)`);
|
|
1213
|
-
const plannedRequiredPubKeysSet = new Set(plannedRequiredPubKeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)));
|
|
1214
|
-
return normalizedSignatures.filter(sig => plannedRequiredPubKeysSet.has((0, uint8array_tools_1.toHex)(sig.pubkey)));
|
|
1215
|
-
};
|
|
1216
|
-
const resolveTaprootSignatureSize = () => {
|
|
1217
|
-
let length;
|
|
1218
|
-
if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
|
|
1219
|
-
length = taprootFakeSignatureSize;
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
const normalizedSignatures = signatures.map(sig => ({
|
|
1223
|
-
pubkey: (0, tapMiniscript_1.normalizeTaprootPubkey)(sig.pubkey),
|
|
1224
|
-
signature: sig.signature
|
|
1225
|
-
}));
|
|
1226
|
-
const internalPubkey = this.getPayment().internalPubkey;
|
|
1227
|
-
if (!internalPubkey) {
|
|
1228
|
-
//addr() of tr addresses may not have internalPubkey
|
|
1229
|
-
if (normalizedSignatures.length !== 1)
|
|
1230
|
-
throw new Error('Error: inputWeight for addr(TR_ADDRESS) requires exactly one signature. Internal taproot pubkey is unavailable in addr() descriptors; use tr(KEY) for strict key matching.');
|
|
1231
|
-
const singleSignature = normalizedSignatures[0];
|
|
1232
|
-
if (!singleSignature)
|
|
1233
|
-
throw new Error('Signatures not present');
|
|
1234
|
-
length = singleSignature.signature.length;
|
|
1235
|
-
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
1236
|
-
}
|
|
1237
|
-
const normalizedInternalPubkey = (0, tapMiniscript_1.normalizeTaprootPubkey)(internalPubkey);
|
|
1238
|
-
const keyPathSignatures = normalizedSignatures.filter(sig => (0, uint8array_tools_1.compare)(sig.pubkey, normalizedInternalPubkey) === 0);
|
|
1239
|
-
if (keyPathSignatures.length !== 1)
|
|
1240
|
-
throw new Error('More than one signture was not expected');
|
|
1241
|
-
const singleSignature = keyPathSignatures[0];
|
|
1242
|
-
if (!singleSignature)
|
|
1243
|
-
throw new Error('Signatures not present');
|
|
1244
|
-
length = singleSignature.signature.length;
|
|
1245
|
-
}
|
|
1246
|
-
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
1247
|
-
};
|
|
1248
|
-
const taprootSpendPath = __classPrivateFieldGet(this, _Output_taprootSpendPath, "f");
|
|
1249
|
-
if (expansion ? expansion.startsWith('pkh(') : isPKH) {
|
|
1250
|
-
return (
|
|
1251
|
-
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (sig:73) + (pubkey:34)
|
|
1252
|
-
(32 + 4 + 4 + 1 + resolveEcdsaSignatureSize() + 34) * 4 +
|
|
1253
|
-
//Segwit:
|
|
1254
|
-
(isSegwitTx ? 1 : 0));
|
|
1255
|
-
}
|
|
1256
|
-
else if (expansion ? expansion.startsWith('wpkh(') : isWPKH) {
|
|
1257
|
-
if (!isSegwitTx)
|
|
1258
|
-
throw new Error('Should be SegwitTx');
|
|
1259
|
-
return (
|
|
1260
|
-
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
|
|
1261
|
-
41 * 4 +
|
|
1262
|
-
// Segwit: (push_count:1) + (sig:73) + (pubkey:34)
|
|
1263
|
-
(1 + resolveEcdsaSignatureSize() + 34));
|
|
1264
|
-
}
|
|
1265
|
-
else if (expansion ? expansion.startsWith('sh(wpkh(') : isSH) {
|
|
1266
|
-
if (!isSegwitTx)
|
|
1267
|
-
throw new Error('Should be SegwitTx');
|
|
1268
|
-
return (
|
|
1269
|
-
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (p2wpkh:23)
|
|
1270
|
-
// -> p2wpkh_script: OP_0 OP_PUSH20 <public_key_hash>
|
|
1271
|
-
// -> p2wpkh: (script_len:1) + (script:22)
|
|
1272
|
-
64 * 4 +
|
|
1273
|
-
// Segwit: (push_count:1) + (sig:73) + (pubkey:34)
|
|
1274
|
-
(1 + resolveEcdsaSignatureSize() + 34));
|
|
1275
|
-
}
|
|
1276
|
-
else if (expansion?.startsWith('sh(wsh(')) {
|
|
1277
|
-
if (!isSegwitTx)
|
|
1278
|
-
throw new Error('Should be SegwitTx');
|
|
1279
|
-
const witnessScript = this.getWitnessScript();
|
|
1280
|
-
if (!witnessScript)
|
|
1281
|
-
throw new Error('sh(wsh) must provide witnessScript');
|
|
1282
|
-
const payment = bitcoinjs_lib_1.payments.p2sh({
|
|
1283
|
-
redeem: bitcoinjs_lib_1.payments.p2wsh({
|
|
1284
|
-
redeem: {
|
|
1285
|
-
input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
|
|
1286
|
-
.scriptSatisfaction,
|
|
1287
|
-
output: witnessScript
|
|
1288
|
-
}
|
|
1289
|
-
})
|
|
1290
|
-
});
|
|
1291
|
-
if (!payment || !payment.input || !payment.witness)
|
|
1292
|
-
throw new Error('Could not create payment');
|
|
1293
|
-
return (
|
|
1294
|
-
//Non-segwit
|
|
1295
|
-
4 * (40 + varSliceSize(payment.input)) +
|
|
1296
|
-
//Segwit
|
|
1297
|
-
vectorSize(payment.witness));
|
|
1298
|
-
}
|
|
1299
|
-
else if (expansion?.startsWith('sh(')) {
|
|
1300
|
-
const redeemScript = this.getRedeemScript();
|
|
1301
|
-
if (!redeemScript)
|
|
1302
|
-
throw new Error('sh() must provide redeemScript');
|
|
1303
|
-
const payment = bitcoinjs_lib_1.payments.p2sh({
|
|
1304
|
-
redeem: {
|
|
1305
|
-
input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
|
|
1306
|
-
.scriptSatisfaction,
|
|
1307
|
-
output: redeemScript
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1310
|
-
if (!payment || !payment.input)
|
|
1311
|
-
throw new Error('Could not create payment');
|
|
1312
|
-
if (payment.witness?.length)
|
|
1313
|
-
throw new Error('A legacy p2sh payment should not cointain a witness');
|
|
1314
|
-
return (
|
|
1315
|
-
//Non-segwit
|
|
1316
|
-
4 * (40 + varSliceSize(payment.input)) +
|
|
1317
|
-
//Segwit:
|
|
1318
|
-
(isSegwitTx ? 1 : 0));
|
|
1319
|
-
}
|
|
1320
|
-
else if (expansion?.startsWith('wsh(')) {
|
|
1321
|
-
const witnessScript = this.getWitnessScript();
|
|
1322
|
-
if (!witnessScript)
|
|
1323
|
-
throw new Error('wsh must provide witnessScript');
|
|
1324
|
-
const payment = bitcoinjs_lib_1.payments.p2wsh({
|
|
1325
|
-
redeem: {
|
|
1326
|
-
input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
|
|
1327
|
-
.scriptSatisfaction,
|
|
1328
|
-
output: witnessScript
|
|
1329
|
-
}
|
|
1330
|
-
});
|
|
1331
|
-
if (!payment || !payment.input || !payment.witness)
|
|
1332
|
-
throw new Error('Could not create payment');
|
|
1333
|
-
return (
|
|
1334
|
-
//Non-segwit
|
|
1335
|
-
4 * (40 + varSliceSize(payment.input)) +
|
|
1336
|
-
//Segwit
|
|
1337
|
-
vectorSize(payment.witness));
|
|
1338
|
-
// when tr(KEY,TREE): choose key-path or script-path based on
|
|
1339
|
-
// constructor taprootSpendPath policy.
|
|
1340
|
-
}
|
|
1341
|
-
else if (expansion?.startsWith('tr(@0,')) {
|
|
1342
|
-
if (!isSegwitTx)
|
|
1343
|
-
throw new Error('Should be SegwitTx');
|
|
1344
|
-
if (taprootSpendPath === 'key')
|
|
1345
|
-
return 41 * 4 + (0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize();
|
|
1346
|
-
const taprootSatisfaction = this.getTapScriptSatisfaction(resolveTaprootSignatures());
|
|
1347
|
-
return 41 * 4 + taprootSatisfaction.totalWitnessSize;
|
|
1348
|
-
}
|
|
1349
|
-
else if (isTR && (!expansion || expansion === 'tr(@0)')) {
|
|
1350
|
-
if (!isSegwitTx)
|
|
1351
|
-
throw new Error('Should be SegwitTx');
|
|
1352
|
-
return (
|
|
1353
|
-
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
|
|
1354
|
-
41 * 4 +
|
|
1355
|
-
// Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64/65))
|
|
1356
|
-
((0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize()));
|
|
1357
|
-
}
|
|
1358
|
-
else {
|
|
1359
|
-
throw new Error(errorMsg);
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
|
-
/**
|
|
1363
|
-
* Computes the Weight Unit contributions of this Output as if it were the
|
|
1364
|
-
* output in a tx.
|
|
1365
|
-
*/
|
|
1366
|
-
outputWeight() {
|
|
1367
|
-
//expand any miniscript-based descriptor. If not miniscript-based, then it's
|
|
1368
|
-
//an addr() descriptor. For those, we can only guess their type.
|
|
1369
|
-
const { isPKH, isWPKH, isSH, isWSH, isTR } = this.guessOutput();
|
|
1370
|
-
const errorMsg = `Output type not implemented. Currently supported: pkh=${isPKH}, wpkh=${isWPKH}, tr=${isTR}, sh=${isSH} and wsh=${isWSH}.`;
|
|
1371
|
-
if (isPKH) {
|
|
1372
|
-
// (p2pkh:26) + (amount:8)
|
|
1373
|
-
return 34 * 4;
|
|
1374
|
-
}
|
|
1375
|
-
else if (isWPKH) {
|
|
1376
|
-
// (p2wpkh:23) + (amount:8)
|
|
1377
|
-
return 31 * 4;
|
|
1378
|
-
}
|
|
1379
|
-
else if (isSH) {
|
|
1380
|
-
// (p2sh:24) + (amount:8)
|
|
1381
|
-
return 32 * 4;
|
|
1382
|
-
}
|
|
1383
|
-
else if (isWSH) {
|
|
1384
|
-
// (p2wsh:35) + (amount:8)
|
|
1385
|
-
return 43 * 4;
|
|
1386
|
-
}
|
|
1387
|
-
else if (isTR) {
|
|
1388
|
-
// (script_pubKey_length:1) + (p2t2(OP_1 OP_PUSH32 <schnorr_public_key>):34) + (amount:8)
|
|
1389
|
-
return 43 * 4;
|
|
1390
|
-
}
|
|
1391
|
-
else {
|
|
1392
|
-
throw new Error(errorMsg);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Sets this output as an input of the provided `psbt` and updates the
|
|
1397
|
-
* `psbt` locktime if required by the descriptor.
|
|
1398
|
-
*
|
|
1399
|
-
* `psbt` and `vout` are mandatory. Include `txHex` as well. The pair
|
|
1400
|
-
* `vout` and `txHex` define the transaction and output number this instance
|
|
1401
|
-
* pertains to.
|
|
1402
|
-
*
|
|
1403
|
-
* Though not advised, for Segwit inputs you can pass `txId` and `value`
|
|
1404
|
-
* in lieu of `txHex`. If doing so, ensure `value` accuracy to avoid
|
|
1405
|
-
* potential fee attacks -
|
|
1406
|
-
* [See this issue](https://github.com/bitcoinjs/bitcoinjs-lib/issues/1625).
|
|
1407
|
-
*
|
|
1408
|
-
* Note: Hardware wallets need the [full `txHex` for Segwit](https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd).
|
|
1409
|
-
*
|
|
1410
|
-
* When unsure, always use `txHex`, and skip `txId` and `value` for safety.
|
|
1411
|
-
*
|
|
1412
|
-
* Use `rbf` to mark whether this tx can be replaced with another with
|
|
1413
|
-
* higher fee while being in the mempool. Note that a tx will automatically
|
|
1414
|
-
* be marked as replacable if a single input requests it.
|
|
1415
|
-
* Note that any transaction using a relative timelock (nSequence < 0x80000000)
|
|
1416
|
-
* also falls within the RBF range (nSequence < 0xFFFFFFFE), making it
|
|
1417
|
-
* inherently replaceable. So don't set `rbf` to false if this is tx uses
|
|
1418
|
-
* relative time locks.
|
|
1419
|
-
*
|
|
1420
|
-
* @returns A finalizer function to be used after signing the `psbt`.
|
|
1421
|
-
* This function ensures that this input is properly finalized.
|
|
1422
|
-
* The finalizer completes the PSBT input by adding the unlocking script
|
|
1423
|
-
* (`scriptWitness` or `scriptSig`) that satisfies this `Output`'s spending
|
|
1424
|
-
* conditions. Because these scripts include signatures, you should finish
|
|
1425
|
-
* all signing operations before calling the finalizer.
|
|
1426
|
-
* The finalizer has this signature:
|
|
1427
|
-
*
|
|
1428
|
-
* `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
|
|
1429
|
-
*
|
|
1430
|
-
*/
|
|
1431
|
-
updatePsbtAsInput({ psbt, txHex, txId, value, vout, //vector output index
|
|
1432
|
-
rbf = true }) {
|
|
1433
|
-
if (value !== undefined && typeof value !== 'bigint')
|
|
1434
|
-
throw new Error(`Error: value must be a bigint`);
|
|
1435
|
-
if (value !== undefined && value < 0n)
|
|
1436
|
-
throw new Error(`Error: value must be >= 0n`);
|
|
1437
|
-
if (txHex === undefined) {
|
|
1438
|
-
console.warn(`Warning: missing txHex may allow fee attacks`);
|
|
1439
|
-
}
|
|
1440
|
-
const isSegwit = this.isSegwit();
|
|
1441
|
-
if (isSegwit === undefined) {
|
|
1442
|
-
//This should only happen when using addr() expressions
|
|
1443
|
-
throw new Error(`Error: could not determine whether this is a segwit descriptor`);
|
|
1444
|
-
}
|
|
1445
|
-
const isTaproot = this.isTaproot();
|
|
1446
|
-
if (isTaproot === undefined) {
|
|
1447
|
-
//This should only happen when using addr() expressions
|
|
1448
|
-
throw new Error(`Error: could not determine whether this is a taproot descriptor`);
|
|
1449
|
-
}
|
|
1450
|
-
const paymentInternalPubkey = this.getPayment().internalPubkey;
|
|
1451
|
-
const tapInternalKey = isTaproot
|
|
1452
|
-
? paymentInternalPubkey
|
|
1453
|
-
? paymentInternalPubkey
|
|
1454
|
-
: undefined
|
|
1455
|
-
: undefined;
|
|
1456
|
-
let tapLeafScript;
|
|
1457
|
-
let tapBip32Derivation;
|
|
1458
|
-
if (isTaproot && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
|
|
1459
|
-
const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
|
|
1460
|
-
if (!tapTreeInfo)
|
|
1461
|
-
throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
|
|
1462
|
-
if (!tapInternalKey)
|
|
1463
|
-
throw new Error(`Error: taprootSpendPath=script requires taproot internal key`);
|
|
1464
|
-
const taprootLeafMetadata = (0, tapMiniscript_1.buildTaprootLeafPsbtMetadata)({
|
|
1465
|
-
tapTreeInfo,
|
|
1466
|
-
internalPubkey: tapInternalKey
|
|
1467
|
-
});
|
|
1468
|
-
tapLeafScript = taprootLeafMetadata.map(({ leaf, controlBlock }) => ({
|
|
1469
|
-
script: leaf.tapScript,
|
|
1470
|
-
leafVersion: leaf.version,
|
|
1471
|
-
controlBlock
|
|
1472
|
-
}));
|
|
1473
|
-
const internalKeyInfo = __classPrivateFieldGet(this, _Output_expansionMap, "f")?.['@0'];
|
|
1474
|
-
if (!internalKeyInfo)
|
|
1475
|
-
throw new Error(`Error: taproot internal key info not available in expansionMap`);
|
|
1476
|
-
tapBip32Derivation = (0, tapMiniscript_1.buildTaprootBip32Derivations)({
|
|
1477
|
-
tapTreeInfo,
|
|
1478
|
-
internalKeyInfo
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
const index = (0, psbt_1.addPsbtInput)({
|
|
1482
|
-
psbt,
|
|
1483
|
-
vout,
|
|
1484
|
-
...(txHex !== undefined ? { txHex } : {}),
|
|
1485
|
-
...(txId !== undefined ? { txId } : {}),
|
|
1486
|
-
...(value !== undefined ? { value } : {}),
|
|
1487
|
-
tapInternalKey,
|
|
1488
|
-
tapLeafScript,
|
|
1489
|
-
tapBip32Derivation,
|
|
1490
|
-
sequence: this.getSequence(),
|
|
1491
|
-
locktime: this.getLockTime(),
|
|
1492
|
-
keysInfo: __classPrivateFieldGet(this, _Output_expansionMap, "f") ? Object.values(__classPrivateFieldGet(this, _Output_expansionMap, "f")) : [],
|
|
1493
|
-
scriptPubKey: this.getScriptPubKey(),
|
|
1494
|
-
isSegwit,
|
|
1495
|
-
witnessScript: this.getWitnessScript(),
|
|
1496
|
-
redeemScript: this.getRedeemScript(),
|
|
1497
|
-
rbf
|
|
1498
|
-
});
|
|
1499
|
-
//The finalizer adds the unlocking script (scriptSig/scriptWitness) once
|
|
1500
|
-
//signatures are ready.
|
|
1501
|
-
const finalizer = ({ psbt, validate = true }) => {
|
|
1502
|
-
if (validate &&
|
|
1503
|
-
!psbt.validateSignaturesOfInput(index, signatureValidator)) {
|
|
1504
|
-
throw new Error(`Error: invalid signatures on input ${index}`);
|
|
1505
|
-
}
|
|
1506
|
-
//An index must be passed since finding the index in the psbt cannot be
|
|
1507
|
-
//done:
|
|
1508
|
-
//Imagine the case where you received money twice to
|
|
1509
|
-
//the same miniscript-based address. You would have the same scriptPubKey,
|
|
1510
|
-
//same sequences, ... The descriptor does not store the hash of the previous
|
|
1511
|
-
//transaction since it is a general Output instance. Indices must be kept
|
|
1512
|
-
//out of the scope of this class and then passed.
|
|
1513
|
-
__classPrivateFieldGet(this, _Output_instances, "m", _Output_assertPsbtInput).call(this, { index, psbt });
|
|
1514
|
-
if (__classPrivateFieldGet(this, _Output_isTaproot, "f") &&
|
|
1515
|
-
__classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script' &&
|
|
1516
|
-
!__classPrivateFieldGet(this, _Output_tapTreeInfo, "f"))
|
|
1517
|
-
throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
|
|
1518
|
-
if (__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
|
|
1519
|
-
const input = psbt.data.inputs[index];
|
|
1520
|
-
const tapLeafScript = input?.tapLeafScript;
|
|
1521
|
-
if (!tapLeafScript || tapLeafScript.length === 0)
|
|
1522
|
-
throw new Error(`Error: cannot finalize taproot script-path without tapLeafScript`);
|
|
1523
|
-
const tapScriptSig = input?.tapScriptSig;
|
|
1524
|
-
if (!tapScriptSig || tapScriptSig.length === 0)
|
|
1525
|
-
throw new Error(`Error: cannot finalize taproot script-path without tapScriptSig`);
|
|
1526
|
-
const taprootSatisfaction = this.getTapScriptSatisfaction(tapScriptSig);
|
|
1527
|
-
const matchingLeaf = tapLeafScript.find(leafScript => (0, uint8array_tools_1.compare)((0, bitcoinjs_lib_internals_1.tapleafHash)({
|
|
1528
|
-
output: leafScript.script,
|
|
1529
|
-
version: leafScript.leafVersion
|
|
1530
|
-
}), taprootSatisfaction.tapLeafHash) === 0);
|
|
1531
|
-
if (!matchingLeaf)
|
|
1532
|
-
throw new Error(`Error: tapLeafScript does not match planned tapLeafHash`);
|
|
1533
|
-
if ((0, uint8array_tools_1.compare)(matchingLeaf.script, taprootSatisfaction.leaf.tapScript) !==
|
|
1534
|
-
0 ||
|
|
1535
|
-
matchingLeaf.leafVersion !== taprootSatisfaction.leaf.version)
|
|
1536
|
-
throw new Error(`Error: tapLeafScript does not match planned leaf script`);
|
|
1537
|
-
const witness = [
|
|
1538
|
-
...taprootSatisfaction.stackItems,
|
|
1539
|
-
matchingLeaf.script,
|
|
1540
|
-
matchingLeaf.controlBlock
|
|
1541
|
-
];
|
|
1542
|
-
const finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(witness);
|
|
1543
|
-
psbt.finalizeTaprootInput(index, taprootSatisfaction.tapLeafHash, () => ({ finalScriptWitness }));
|
|
1544
|
-
}
|
|
1545
|
-
else if (!__classPrivateFieldGet(this, _Output_miniscript, "f")) {
|
|
1546
|
-
//Use standard finalizers
|
|
1547
|
-
psbt.finalizeInput(index);
|
|
1548
|
-
}
|
|
1549
|
-
else {
|
|
1550
|
-
const signatures = psbt.data.inputs[index]?.partialSig;
|
|
1551
|
-
if (!signatures)
|
|
1552
|
-
throw new Error(`Error: cannot finalize without signatures`);
|
|
1553
|
-
const { scriptSatisfaction } = this.getScriptSatisfaction(signatures);
|
|
1554
|
-
psbt.finalizeInput(index, (0, psbt_1.finalScriptsFuncFactory)(scriptSatisfaction, __classPrivateFieldGet(this, _Output_network, "f")));
|
|
1555
|
-
}
|
|
1556
|
-
};
|
|
1557
|
-
return finalizer;
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* Adds this output as an output of the provided `psbt` with the given
|
|
1561
|
-
* value.
|
|
1562
|
-
* @param params - The parameters for the method.
|
|
1563
|
-
* @param params.psbt - The Partially Signed Bitcoin Transaction.
|
|
1564
|
-
* @param params.value - The value for the output in satoshis.
|
|
1565
|
-
*/
|
|
1566
|
-
updatePsbtAsOutput({ psbt, value }) {
|
|
1567
|
-
if (typeof value !== 'bigint')
|
|
1568
|
-
throw new Error(`Error: value must be a bigint`);
|
|
1569
|
-
if (value < 0n)
|
|
1570
|
-
throw new Error(`Error: value must be >= 0n`);
|
|
1571
|
-
psbt.addOutput({ script: this.getScriptPubKey(), value });
|
|
1572
|
-
}
|
|
1573
|
-
/**
|
|
1574
|
-
* Decomposes the descriptor used to form this `Output` into its elemental
|
|
1575
|
-
* parts. See {@link ExpansionMap ExpansionMap} for a detailed explanation.
|
|
1576
|
-
*/
|
|
1577
|
-
expand() {
|
|
1578
|
-
return {
|
|
1579
|
-
...(__classPrivateFieldGet(this, _Output_expandedExpression, "f") !== undefined
|
|
1580
|
-
? { expandedExpression: __classPrivateFieldGet(this, _Output_expandedExpression, "f") }
|
|
1581
|
-
: {}),
|
|
1582
|
-
...(__classPrivateFieldGet(this, _Output_miniscript, "f") !== undefined
|
|
1583
|
-
? { miniscript: __classPrivateFieldGet(this, _Output_miniscript, "f") }
|
|
1584
|
-
: {}),
|
|
1585
|
-
...(__classPrivateFieldGet(this, _Output_expandedMiniscript, "f") !== undefined
|
|
1586
|
-
? { expandedMiniscript: __classPrivateFieldGet(this, _Output_expandedMiniscript, "f") }
|
|
1587
|
-
: {}),
|
|
1588
|
-
...(__classPrivateFieldGet(this, _Output_tapTreeExpression, "f") !== undefined
|
|
1589
|
-
? { tapTreeExpression: __classPrivateFieldGet(this, _Output_tapTreeExpression, "f") }
|
|
1590
|
-
: {}),
|
|
1591
|
-
...(__classPrivateFieldGet(this, _Output_tapTree, "f") !== undefined ? { tapTree: __classPrivateFieldGet(this, _Output_tapTree, "f") } : {}),
|
|
1592
|
-
...(__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") !== undefined
|
|
1593
|
-
? { tapTreeInfo: __classPrivateFieldGet(this, _Output_tapTreeInfo, "f") }
|
|
1594
|
-
: {}),
|
|
1595
|
-
...(__classPrivateFieldGet(this, _Output_expansionMap, "f") !== undefined
|
|
1596
|
-
? { expansionMap: __classPrivateFieldGet(this, _Output_expansionMap, "f") }
|
|
1597
|
-
: {})
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
_Output_payment = new WeakMap(), _Output_preimages = new WeakMap(), _Output_signersPubKeys = new WeakMap(), _Output_miniscript = new WeakMap(), _Output_witnessScript = new WeakMap(), _Output_redeemScript = new WeakMap(), _Output_isSegwit = new WeakMap(), _Output_isTaproot = new WeakMap(), _Output_expandedExpression = new WeakMap(), _Output_expandedMiniscript = new WeakMap(), _Output_tapTreeExpression = new WeakMap(), _Output_tapTree = new WeakMap(), _Output_tapTreeInfo = new WeakMap(), _Output_taprootSpendPath = new WeakMap(), _Output_tapLeaf = new WeakMap(), _Output_expansionMap = new WeakMap(), _Output_network = new WeakMap(), _Output_instances = new WeakSet(), _Output_resolveMiniscriptSignersPubKeys = function _Output_resolveMiniscriptSignersPubKeys() {
|
|
1602
|
-
//If the user did not provide a pubkey subset (signersPubKeys), assume all
|
|
1603
|
-
//miniscript pubkeys can sign.
|
|
1604
|
-
if (__classPrivateFieldGet(this, _Output_signersPubKeys, "f"))
|
|
1605
|
-
return __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
|
|
1606
|
-
const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
|
|
1607
|
-
if (!expansionMap)
|
|
1608
|
-
throw new Error(`Error: expansionMap not available for miniscript`);
|
|
1609
|
-
return Object.values(expansionMap).map(keyInfo => {
|
|
1610
|
-
const pubkey = keyInfo.pubkey;
|
|
1611
|
-
if (!pubkey)
|
|
1612
|
-
throw new Error(`Error: miniscript key missing pubkey`);
|
|
1613
|
-
return pubkey;
|
|
1614
|
-
});
|
|
1615
|
-
}, _Output_assertMiniscriptSatisfactionResourceLimits = function _Output_assertMiniscriptSatisfactionResourceLimits(scriptSatisfaction) {
|
|
1616
|
-
if (!__classPrivateFieldGet(this, _Output_miniscript, "f"))
|
|
1617
|
-
return;
|
|
1618
|
-
const satisfactionStackItems = bitcoinjs_lib_1.script.toStack(scriptSatisfaction);
|
|
1619
|
-
// For wsh(...) and sh(wsh(...)), enforce witness stack limits.
|
|
1620
|
-
if (__classPrivateFieldGet(this, _Output_isSegwit, "f") && !__classPrivateFieldGet(this, _Output_isTaproot, "f")) {
|
|
1621
|
-
(0, resourceLimits_1.assertWitnessV0SatisfactionResourceLimits)({
|
|
1622
|
-
stackItems: satisfactionStackItems
|
|
1623
|
-
});
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
if (!__classPrivateFieldGet(this, _Output_isSegwit, "f")) {
|
|
1627
|
-
// In legacy P2SH, after scriptSig execution, the stack is:
|
|
1628
|
-
// [ ...satisfactionStackItems, redeemScript ]
|
|
1629
|
-
// Consensus limits apply here: each element must be <= 520 bytes and total
|
|
1630
|
-
// stack items must be <= 1000.
|
|
1631
|
-
// redeemScript size is already validated during script construction
|
|
1632
|
-
// to fail early (look for MAX_SCRIPT_ELEMENT_SIZE checks in this file).
|
|
1633
|
-
// Below we re-validate again the redeemScript + the rest of stack items.
|
|
1634
|
-
const redeemScript = __classPrivateFieldGet(this, _Output_redeemScript, "f");
|
|
1635
|
-
if (!redeemScript)
|
|
1636
|
-
throw new Error(`Error: redeemScript not available for P2SH spend`);
|
|
1637
|
-
(0, resourceLimits_1.assertConsensusStackResourceLimits)({
|
|
1638
|
-
stackItems: [...satisfactionStackItems, redeemScript]
|
|
1639
|
-
});
|
|
1640
|
-
(0, resourceLimits_1.assertP2shScriptSigStandardSize)({
|
|
1641
|
-
scriptSatisfaction,
|
|
1642
|
-
redeemScript,
|
|
1643
|
-
network: __classPrivateFieldGet(this, _Output_network, "f")
|
|
1644
|
-
});
|
|
1645
|
-
}
|
|
1646
|
-
}, _Output_resolveTapTreeSignersPubKeys = function _Output_resolveTapTreeSignersPubKeys() {
|
|
1647
|
-
//If the user did not provide a pubkey subset (signersPubKeys), assume all
|
|
1648
|
-
//taproot leaf pubkeys can sign.
|
|
1649
|
-
const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
|
|
1650
|
-
if (!tapTreeInfo)
|
|
1651
|
-
throw new Error(`Error: taproot tree info not available`);
|
|
1652
|
-
const candidatePubkeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f")
|
|
1653
|
-
? __classPrivateFieldGet(this, _Output_signersPubKeys, "f").map(tapMiniscript_1.normalizeTaprootPubkey)
|
|
1654
|
-
: (0, tapMiniscript_1.collectTapTreePubkeys)(tapTreeInfo);
|
|
1655
|
-
return Array.from(new Set(candidatePubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)))).map(hex => (0, uint8array_tools_1.fromHex)(hex));
|
|
1656
|
-
}, _Output_getConstraints = function _Output_getConstraints() {
|
|
1657
|
-
const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
|
|
1658
|
-
const preimages = __classPrivateFieldGet(this, _Output_preimages, "f");
|
|
1659
|
-
const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
|
|
1660
|
-
const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
|
|
1661
|
-
const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
|
|
1662
|
-
//Create a method. solvePreimages to solve them.
|
|
1663
|
-
if (miniscript) {
|
|
1664
|
-
if (expandedMiniscript === undefined || expansionMap === undefined)
|
|
1665
|
-
throw new Error(`Error: cannot get time constraints from not expanded miniscript ${miniscript}`);
|
|
1666
|
-
//We create some fakeSignatures since we may not have them yet.
|
|
1667
|
-
//We only want to retrieve the nLockTime and nSequence of the satisfaction and
|
|
1668
|
-
//signatures don't matter
|
|
1669
|
-
const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).map(pubkey => ({
|
|
1670
|
-
pubkey,
|
|
1671
|
-
signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
|
|
1672
|
-
}));
|
|
1673
|
-
const satisfaction = (0, miniscript_1.satisfyMiniscript)({
|
|
1674
|
-
expandedMiniscript,
|
|
1675
|
-
expansionMap,
|
|
1676
|
-
signatures: fakeSignatures,
|
|
1677
|
-
preimages
|
|
1678
|
-
});
|
|
1679
|
-
__classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
|
|
1680
|
-
const { nLockTime, nSequence } = satisfaction;
|
|
1681
|
-
return { nLockTime, nSequence, tapLeafHash: undefined };
|
|
1682
|
-
}
|
|
1683
|
-
else if (tapTreeInfo && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
|
|
1684
|
-
const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
|
|
1685
|
-
pubkey,
|
|
1686
|
-
signature: new Uint8Array(TAPROOT_FAKE_SIGNATURE_SIZE)
|
|
1687
|
-
}));
|
|
1688
|
-
const { nLockTime, nSequence, tapLeafHash } = (0, tapMiniscript_1.satisfyTapTree)({
|
|
1689
|
-
tapTreeInfo,
|
|
1690
|
-
preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
|
|
1691
|
-
signatures: fakeSignatures,
|
|
1692
|
-
...(__classPrivateFieldGet(this, _Output_tapLeaf, "f") !== undefined ? { tapLeaf: __classPrivateFieldGet(this, _Output_tapLeaf, "f") } : {})
|
|
1693
|
-
});
|
|
1694
|
-
return { nLockTime, nSequence, tapLeafHash };
|
|
1695
|
-
}
|
|
1696
|
-
return undefined;
|
|
1697
|
-
}, _Output_assertPsbtInput = function _Output_assertPsbtInput({ psbt, index }) {
|
|
1698
|
-
const input = psbt.data.inputs[index];
|
|
1699
|
-
const txInput = psbt.txInputs[index];
|
|
1700
|
-
if (!input || !txInput)
|
|
1701
|
-
throw new Error(`Error: invalid input or txInput`);
|
|
1702
|
-
const { sequence: inputSequence, index: vout } = txInput;
|
|
1703
|
-
let scriptPubKey;
|
|
1704
|
-
if (input.witnessUtxo)
|
|
1705
|
-
scriptPubKey = input.witnessUtxo.script;
|
|
1706
|
-
else {
|
|
1707
|
-
if (!input.nonWitnessUtxo)
|
|
1708
|
-
throw new Error(`Error: input should have either witnessUtxo or nonWitnessUtxo`);
|
|
1709
|
-
const tx = bitcoinjs_lib_1.Transaction.fromBuffer(input.nonWitnessUtxo);
|
|
1710
|
-
const out = tx.outs[vout];
|
|
1711
|
-
if (!out)
|
|
1712
|
-
throw new Error(`Error: utxo should exist`);
|
|
1713
|
-
scriptPubKey = out.script;
|
|
1714
|
-
}
|
|
1715
|
-
const locktime = this.getLockTime() || 0;
|
|
1716
|
-
const sequence = this.getSequence();
|
|
1717
|
-
//We don't know whether the user opted for RBF or not. So check that
|
|
1718
|
-
//at least one of the 2 sequences matches.
|
|
1719
|
-
const sequenceNoRBF = sequence !== undefined
|
|
1720
|
-
? sequence
|
|
1721
|
-
: locktime === 0
|
|
1722
|
-
? 0xffffffff
|
|
1723
|
-
: 0xfffffffe;
|
|
1724
|
-
const sequenceRBF = sequence !== undefined ? sequence : 0xfffffffd;
|
|
1725
|
-
const eqBytes = (bytes1, bytes2) => bytes1 === undefined || bytes2 === undefined
|
|
1726
|
-
? bytes1 === bytes2
|
|
1727
|
-
: (0, uint8array_tools_1.compare)(bytes1, bytes2) === 0;
|
|
1728
|
-
if ((0, uint8array_tools_1.compare)(scriptPubKey, this.getScriptPubKey()) !== 0 ||
|
|
1729
|
-
(sequenceRBF !== inputSequence && sequenceNoRBF !== inputSequence) ||
|
|
1730
|
-
locktime !== psbt.locktime ||
|
|
1731
|
-
!eqBytes(this.getWitnessScript(), input.witnessScript) ||
|
|
1732
|
-
!eqBytes(this.getRedeemScript(), input.redeemScript)) {
|
|
1733
|
-
throw new Error(`Error: cannot finalize psbt index ${index} since it does not correspond to this descriptor`);
|
|
1734
|
-
}
|
|
1735
|
-
};
|
|
1736
|
-
return {
|
|
1737
|
-
Output,
|
|
1738
|
-
parseKeyExpression,
|
|
1739
|
-
expand,
|
|
1740
|
-
ECPair,
|
|
1741
|
-
BIP32
|
|
1742
|
-
};
|
|
1743
|
-
}
|