@bitcoinerlab/descriptors 2.3.6 → 3.0.1

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.
@@ -43,6 +43,7 @@ const bitcoinjs_lib_1 = require("bitcoinjs-lib");
43
43
  const keyExpressions_1 = require("./keyExpressions");
44
44
  const RE = __importStar(require("./re"));
45
45
  const miniscript_1 = require("@bitcoinerlab/miniscript");
46
+ const uint8array_tools_1 = require("uint8array-tools");
46
47
  /**
47
48
  * Expand a miniscript to a generalized form using variables instead of key
48
49
  * expressions. Variables will be of this form: @0, @1, ...
@@ -51,24 +52,55 @@ const miniscript_1 = require("@bitcoinerlab/miniscript");
51
52
  * Also compute pubkeys from descriptors to use them later.
52
53
  */
53
54
  function expandMiniscript({ miniscript, isSegwit, isTaproot, network = bitcoinjs_lib_1.networks.bitcoin, ECPair, BIP32 }) {
54
- if (isTaproot)
55
- throw new Error('Taproot miniscript not yet supported.');
56
55
  const reKeyExp = isTaproot
57
56
  ? RE.reTaprootKeyExp
58
57
  : isSegwit
59
58
  ? RE.reSegwitKeyExp
60
59
  : RE.reNonSegwitKeyExp;
61
60
  const expansionMap = {};
62
- const expandedMiniscript = miniscript.replace(RegExp(reKeyExp, 'g'), (keyExpression) => {
63
- const key = '@' + Object.keys(expansionMap).length;
61
+ let keyIndex = 0;
62
+ const keyExpressionRegex = new RegExp(String.raw `^${reKeyExp}$`);
63
+ const replaceKeyExpression = (keyExpression) => {
64
+ const trimmed = keyExpression.trim();
65
+ if (!trimmed)
66
+ throw new Error(`Error: expected a keyExpression but got ${keyExpression}`);
67
+ if (!keyExpressionRegex.test(trimmed))
68
+ throw new Error(`Error: expected a keyExpression but got ${trimmed}`);
69
+ const key = `@${keyIndex}`;
70
+ keyIndex += 1;
64
71
  expansionMap[key] = (0, keyExpressions_1.parseKeyExpression)({
65
- keyExpression,
72
+ keyExpression: trimmed,
66
73
  isSegwit,
74
+ isTaproot,
67
75
  network,
68
76
  ECPair,
69
77
  BIP32
70
78
  });
71
79
  return key;
80
+ };
81
+ // These are the Miniscript fragments where key arguments are expected.
82
+ // We only replace key expressions inside these fragments to avoid confusing
83
+ // hash preimages (e.g. sha256(03...)) with keys.
84
+ //
85
+ // Note: sorted script expressions (`sortedmulti`, `sortedmulti_a`) are
86
+ // intentionally excluded here. They are descriptor-level expressions (not
87
+ // Miniscript fragments) and are handled in descriptor/tap-tree code paths
88
+ // before Miniscript compilation.
89
+ const keyBearingFragmentRegex = /\b(pk|pkh|multi_a|multi)\(([^()]*)\)/g;
90
+ const expandedMiniscript = miniscript.replace(keyBearingFragmentRegex, (_, name, inner) => {
91
+ if (name === 'pk' || name === 'pkh')
92
+ return `${name}(${replaceKeyExpression(inner)})`;
93
+ //now do *multi* which has arguments:
94
+ const parts = inner.split(',').map(part => part.trim());
95
+ if (parts.length < 2)
96
+ throw new Error(`Error: invalid miniscript ${miniscript} (missing keys)`);
97
+ const k = parts[0] ?? '';
98
+ if (!k)
99
+ throw new Error(`Error: invalid miniscript ${miniscript} (missing threshold)`);
100
+ const replacedKeys = parts
101
+ .slice(1)
102
+ .map(keyExpression => replaceKeyExpression(keyExpression));
103
+ return `${name}(${[k, ...replacedKeys].join(',')})`;
72
104
  });
73
105
  //Do some assertions. Miniscript must not have duplicate keys, also all
74
106
  //keyExpressions must produce a valid pubkey (unless it's ranged and we want
@@ -78,7 +110,7 @@ function expandMiniscript({ miniscript, isSegwit, isTaproot, network = bitcoinjs
78
110
  .map(keyInfo => {
79
111
  if (!keyInfo.pubkey)
80
112
  throw new Error(`Error: keyExpression ${keyInfo.keyExpression} does not have a pubkey`);
81
- return keyInfo.pubkey.toString('hex');
113
+ return (0, uint8array_tools_1.toHex)(keyInfo.pubkey);
82
114
  });
83
115
  if (new Set(pubkeysHex).size !== pubkeysHex.length) {
84
116
  throw new Error(`Error: miniscript ${miniscript} is not sane: contains duplicate public keys.`);
@@ -100,8 +132,8 @@ function substituteAsm({ expandedAsm, expansionMap }) {
100
132
  throw new Error(`Error: invalid expansionMap for ${key}`);
101
133
  }
102
134
  return accAsm
103
- .replaceAll(`<${key}>`, `<${pubkey.toString('hex')}>`)
104
- .replaceAll(`<HASH160(${key})>`, `<${bitcoinjs_lib_1.crypto.hash160(pubkey).toString('hex')}>`);
135
+ .replaceAll(`<${key}>`, `<${(0, uint8array_tools_1.toHex)(pubkey)}>`)
136
+ .replaceAll(`<HASH160(${key})>`, `<${(0, uint8array_tools_1.toHex)(bitcoinjs_lib_1.crypto.hash160(pubkey))}>`);
105
137
  }, expandedAsm);
106
138
  //Now clean it and prepare it so that fromASM can be called:
107
139
  asm = asm
@@ -119,8 +151,8 @@ function substituteAsm({ expandedAsm, expansionMap }) {
119
151
  .replace(/[<>]/g, '');
120
152
  return asm;
121
153
  }
122
- function miniscript2Script({ expandedMiniscript, expansionMap }) {
123
- const compiled = (0, miniscript_1.compileMiniscript)(expandedMiniscript);
154
+ function miniscript2Script({ expandedMiniscript, expansionMap, tapscript = false }) {
155
+ const compiled = (0, miniscript_1.compileMiniscript)(expandedMiniscript, { tapscript });
124
156
  if (compiled.issane !== true) {
125
157
  throw new Error(`Error: Miniscript ${expandedMiniscript} is not sane`);
126
158
  }
@@ -137,12 +169,24 @@ function miniscript2Script({ expandedMiniscript, expansionMap }) {
137
169
  * Pass timeConstraints to search for the first solution with this nLockTime and
138
170
  * nSequence. Throw if no solution is possible using these constraints.
139
171
  *
172
+ * Time constraints are used to keep the chosen satisfaction stable between the
173
+ * planning pass (fake signatures) and the signing pass (real signatures).
174
+ * We run the satisfier once with fake signatures to discover the implied
175
+ * nLockTime/nSequence without requiring user signatures. If real signatures
176
+ * had the same length, the satisfier would typically pick the same
177
+ * minimal-weight solution again. But ECDSA signature sizes can vary (71–73
178
+ * bytes), which may change which solution is considered "smallest".
179
+ *
180
+ * Passing the previously derived timeConstraints in the second pass forces the
181
+ * same solution to be selected, ensuring locktime/sequence do not change
182
+ * between planning and finalization.
183
+ *
140
184
  * Don't pass timeConstraints (this is the default) if you want to get the
141
185
  * smallest size solution altogether.
142
186
  *
143
187
  * If a solution is not found this function throws.
144
188
  */
145
- function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [], preimages = [], timeConstraints }) {
189
+ function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [], preimages = [], timeConstraints, tapscript = false }) {
146
190
  //convert 'sha256(6c...33)' to: { ['<sha256_preimage(6c...33)>']: '10...5f'}
147
191
  const preimageMap = {};
148
192
  preimages.forEach(preimage => {
@@ -153,15 +197,18 @@ function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [],
153
197
  //get the keyExpressions: @0, @1 from the keys in expansionMap
154
198
  const expandedSignatureMap = {};
155
199
  signatures.forEach(signature => {
156
- const pubkeyHex = signature.pubkey.toString('hex');
157
- const keyExpression = Object.keys(expansionMap).find(k => expansionMap[k]?.pubkey?.toString('hex') === pubkeyHex);
200
+ const pubkeyHex = (0, uint8array_tools_1.toHex)(signature.pubkey);
201
+ const keyExpression = Object.keys(expansionMap).find(k => expansionMap[k]?.pubkey && (0, uint8array_tools_1.toHex)(expansionMap[k].pubkey) === pubkeyHex);
158
202
  expandedSignatureMap['<sig(' + keyExpression + ')>'] =
159
- '<' + signature.signature.toString('hex') + '>';
203
+ '<' + (0, uint8array_tools_1.toHex)(signature.signature) + '>';
160
204
  });
161
205
  const expandedKnownsMap = { ...preimageMap, ...expandedSignatureMap };
162
206
  const knowns = Object.keys(expandedKnownsMap);
163
207
  //satisfier verifies again internally whether expandedKnownsMap with given knowns is sane
164
- const { nonMalleableSats } = (0, miniscript_1.satisfier)(expandedMiniscript, { knowns });
208
+ const { nonMalleableSats } = (0, miniscript_1.satisfier)(expandedMiniscript, {
209
+ knowns,
210
+ tapscript
211
+ });
165
212
  if (!Array.isArray(nonMalleableSats) || !nonMalleableSats[0])
166
213
  throw new Error(`Error: unresolvable miniscript ${expandedMiniscript}`);
167
214
  let sat;
@@ -218,7 +265,7 @@ function satisfyMiniscript({ expandedMiniscript, expansionMap, signatures = [],
218
265
  * However, the `0` number is an edge case that we specially handle with this
219
266
  * function.
220
267
  *
221
- * bitcoinjs-lib's `bscript.number.encode(0)` produces an empty Buffer.
268
+ * bitcoinjs-lib's `bscript.number.encode(0)` produces an empty array.
222
269
  * This is what the Bitcoin interpreter does and it is what `script.number.encode` was
223
270
  * implemented to do.
224
271
  *
@@ -254,5 +301,5 @@ function numberEncodeAsm(number) {
254
301
  return 'OP_0';
255
302
  }
256
303
  else
257
- return bitcoinjs_lib_1.script.number.encode(number).toString('hex');
304
+ return (0, uint8array_tools_1.toHex)(bitcoinjs_lib_1.script.number.encode(number));
258
305
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolves all multipath tuple segments (for example `/<0;1>/*`) in lockstep
3
+ * using the provided `change` value.
4
+ *
5
+ * - `/**` is first canonicalized to `/<0;1>/*`.
6
+ * - All tuples in the descriptor must have the same cardinality.
7
+ * - Tuple values must be strictly increasing decimal numbers.
8
+ * - `change` must match one of the values in each tuple.
9
+ */
10
+ export declare function resolveMultipathDescriptor({ descriptor, change }: {
11
+ descriptor: string;
12
+ change?: number;
13
+ }): string;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveMultipathDescriptor = resolveMultipathDescriptor;
4
+ /**
5
+ * Replaces the receive/change shorthand `/**` with its canonical multipath
6
+ * representation `/<0;1>/*`.
7
+ */
8
+ function expandReceiveChangeShorthand(descriptor) {
9
+ return descriptor.replace(/\/\*\*/g, '/<0;1>/*');
10
+ }
11
+ /**
12
+ * Parses a multipath tuple body from `<...>`.
13
+ *
14
+ * This implementation intentionally accepts only decimal numbers (no hardened
15
+ * suffixes) and enforces strict left-to-right increase as a safety feature to
16
+ * catch likely human errors such as `<1;0>`.
17
+ */
18
+ function parseMultipathTuple(tupleBody) {
19
+ const parts = tupleBody.split(';');
20
+ if (parts.length < 2)
21
+ throw new Error(`Error: multipath tuple must contain at least 2 values, got <${tupleBody}>`);
22
+ const values = parts.map(part => {
23
+ if (!/^(0|[1-9]\d*)$/.test(part))
24
+ throw new Error(`Error: multipath tuple values must be decimal numbers, got <${tupleBody}>`);
25
+ const value = Number(part);
26
+ if (!Number.isSafeInteger(value))
27
+ throw new Error(`Error: multipath tuple value overflow, got <${tupleBody}>`);
28
+ return value;
29
+ });
30
+ for (let i = 1; i < values.length; i++) {
31
+ const prev = values[i - 1];
32
+ const current = values[i];
33
+ if (prev === undefined || current === undefined)
34
+ throw new Error(`Error: invalid multipath tuple <${tupleBody}>`);
35
+ if (current <= prev)
36
+ throw new Error(`Error: multipath tuple values must be strictly increasing from left to right, got <${tupleBody}>`);
37
+ }
38
+ return values;
39
+ }
40
+ /**
41
+ * Resolves all multipath tuple segments (for example `/<0;1>/*`) in lockstep
42
+ * using the provided `change` value.
43
+ *
44
+ * - `/**` is first canonicalized to `/<0;1>/*`.
45
+ * - All tuples in the descriptor must have the same cardinality.
46
+ * - Tuple values must be strictly increasing decimal numbers.
47
+ * - `change` must match one of the values in each tuple.
48
+ */
49
+ function resolveMultipathDescriptor({ descriptor, change }) {
50
+ const canonicalDescriptor = expandReceiveChangeShorthand(descriptor);
51
+ const tupleMatches = Array.from(canonicalDescriptor.matchAll(/\/<([^<>]+)>/g), match => ({
52
+ token: match[0],
53
+ tupleBody: match[1],
54
+ values: parseMultipathTuple(match[1])
55
+ }));
56
+ if (tupleMatches.length === 0)
57
+ return canonicalDescriptor;
58
+ if (change === undefined)
59
+ throw new Error(`Error: change was not provided for multipath descriptor`);
60
+ if (!Number.isInteger(change) || change < 0)
61
+ throw new Error(`Error: invalid change ${change}`);
62
+ const tupleSize = tupleMatches[0]?.values.length;
63
+ if (!tupleSize)
64
+ throw new Error(`Error: invalid multipath tuple`);
65
+ for (const tupleMatch of tupleMatches) {
66
+ if (tupleMatch.values.length !== tupleSize)
67
+ throw new Error(`Error: all multipath tuples must have the same number of options`);
68
+ }
69
+ let resolvedDescriptor = canonicalDescriptor;
70
+ for (const tupleMatch of tupleMatches) {
71
+ if (!tupleMatch.values.includes(change))
72
+ throw new Error(`Error: change ${change} not found in multipath tuple <${tupleMatch.tupleBody}>`);
73
+ resolvedDescriptor = resolvedDescriptor.replaceAll(tupleMatch.token, `/${change}`);
74
+ }
75
+ return resolvedDescriptor;
76
+ }
@@ -0,0 +1,3 @@
1
+ import type { Network } from 'bitcoinjs-lib';
2
+ export declare function isBitcoinMainnet(network: Network): boolean;
3
+ export declare function coinTypeFromNetwork(network: Network): 0 | 1;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isBitcoinMainnet = isBitcoinMainnet;
4
+ exports.coinTypeFromNetwork = coinTypeFromNetwork;
5
+ const bitcoinjs_lib_1 = require("bitcoinjs-lib");
6
+ function isBitcoinMainnet(network) {
7
+ return (network.bech32 === bitcoinjs_lib_1.networks.bitcoin.bech32 &&
8
+ network.bip32.public === bitcoinjs_lib_1.networks.bitcoin.bip32.public &&
9
+ network.bip32.private === bitcoinjs_lib_1.networks.bitcoin.bip32.private &&
10
+ network.pubKeyHash === bitcoinjs_lib_1.networks.bitcoin.pubKeyHash &&
11
+ network.scriptHash === bitcoinjs_lib_1.networks.bitcoin.scriptHash &&
12
+ network.wif === bitcoinjs_lib_1.networks.bitcoin.wif);
13
+ }
14
+ function coinTypeFromNetwork(network) {
15
+ return isBitcoinMainnet(network) ? 0 : 1;
16
+ }
@@ -0,0 +1,7 @@
1
+ export declare function splitTopLevelComma({ expression, onError }: {
2
+ expression: string;
3
+ onError: (expression: string) => Error;
4
+ }): {
5
+ left: string;
6
+ right: string;
7
+ } | null;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitTopLevelComma = splitTopLevelComma;
4
+ function splitTopLevelComma({ expression, onError }) {
5
+ let braceDepth = 0;
6
+ let parenDepth = 0;
7
+ let commaIndex = -1;
8
+ for (let i = 0; i < expression.length; i++) {
9
+ const char = expression[i];
10
+ if (!char)
11
+ continue;
12
+ if (char === '{') {
13
+ braceDepth++;
14
+ }
15
+ else if (char === '}') {
16
+ if (braceDepth === 0)
17
+ throw onError(expression);
18
+ braceDepth--;
19
+ }
20
+ else if (char === '(') {
21
+ //Track miniscript argument lists so we don't split on commas inside them.
22
+ parenDepth++;
23
+ }
24
+ else if (char === ')') {
25
+ if (parenDepth === 0)
26
+ throw onError(expression);
27
+ parenDepth--;
28
+ }
29
+ else if (char === ',') {
30
+ if (braceDepth === 0 && parenDepth === 0) {
31
+ if (commaIndex !== -1)
32
+ throw onError(expression);
33
+ commaIndex = i;
34
+ }
35
+ }
36
+ }
37
+ if (braceDepth !== 0 || parenDepth !== 0)
38
+ throw onError(expression);
39
+ if (commaIndex === -1)
40
+ return null;
41
+ const left = expression.slice(0, commaIndex).trim();
42
+ const right = expression.slice(commaIndex + 1).trim();
43
+ if (!left || !right)
44
+ throw onError(expression);
45
+ return { left, right };
46
+ }
package/dist/psbt.d.ts CHANGED
@@ -1,40 +1,44 @@
1
- import type { PsbtInput } from 'bip174/src/lib/interfaces';
1
+ import type { PsbtInput, TapBip32Derivation, TapLeafScript } from 'bip174';
2
2
  import type { KeyInfo } from './types';
3
3
  import { Network, Psbt } from 'bitcoinjs-lib';
4
4
  /**
5
5
  * This function must do two things:
6
6
  * 1. Check if the `input` can be finalized. If it can not be finalized, throw.
7
7
  * ie. `Can not finalize input #${inputIndex}`
8
- * 2. Create the finalScriptSig and finalScriptWitness Buffers.
8
+ * 2. Create finalScriptSig and finalScriptWitness.
9
9
  */
10
10
  type FinalScriptsFunc = (inputIndex: number, // Which input is it?
11
11
  input: PsbtInput, // The PSBT input contents
12
- script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
12
+ script: Uint8Array, // The "meaningful" locking script (redeemScript for P2SH etc.)
13
13
  isSegwit: boolean, // Is it segwit?
14
14
  isP2SH: boolean, // Is it P2SH?
15
15
  isP2WSH: boolean) => {
16
- finalScriptSig: Buffer | undefined;
17
- finalScriptWitness: Buffer | undefined;
16
+ finalScriptSig: Uint8Array | undefined;
17
+ finalScriptWitness: Uint8Array | undefined;
18
18
  };
19
- export declare function finalScriptsFuncFactory(scriptSatisfaction: Buffer, network: Network): FinalScriptsFunc;
19
+ export declare function finalScriptsFuncFactory(scriptSatisfaction: Uint8Array, network: Network): FinalScriptsFunc;
20
20
  /**
21
- * Important: Read comments on descriptor.updatePsbt regarding not passing txHex
21
+ * Important: Read comments on Output.updatePsbtAsInput regarding not passing txHex
22
22
  */
23
- export declare function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, witnessScript, redeemScript, rbf }: {
23
+ export declare function addPsbtInput({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, tapLeafScript, tapBip32Derivation, witnessScript, redeemScript, rbf }: {
24
24
  psbt: Psbt;
25
25
  vout: number;
26
26
  txHex?: string;
27
27
  txId?: string;
28
- value?: number;
28
+ value?: bigint;
29
29
  sequence: number | undefined;
30
30
  locktime: number | undefined;
31
31
  keysInfo: KeyInfo[];
32
- scriptPubKey: Buffer;
32
+ scriptPubKey: Uint8Array;
33
33
  isSegwit: boolean;
34
34
  /** for taproot **/
35
- tapInternalKey?: Buffer | undefined;
36
- witnessScript: Buffer | undefined;
37
- redeemScript: Buffer | undefined;
35
+ tapInternalKey?: Uint8Array | undefined;
36
+ /** for taproot script-path **/
37
+ tapLeafScript?: TapLeafScript[] | undefined;
38
+ /** for taproot **/
39
+ tapBip32Derivation?: TapBip32Derivation[] | undefined;
40
+ witnessScript: Uint8Array | undefined;
41
+ redeemScript: Uint8Array | undefined;
38
42
  rbf: boolean;
39
43
  }): number;
40
44
  export {};
package/dist/psbt.js CHANGED
@@ -3,43 +3,23 @@
3
3
  // Distributed under the MIT software license
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.finalScriptsFuncFactory = finalScriptsFuncFactory;
6
- exports.updatePsbt = updatePsbt;
6
+ exports.addPsbtInput = addPsbtInput;
7
7
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
8
- const varuint_bitcoin_1 = require("varuint-bitcoin");
9
- function reverseBuffer(buffer) {
8
+ const uint8array_tools_1 = require("uint8array-tools");
9
+ const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
10
+ function reverseBytes(buffer) {
10
11
  if (buffer.length < 1)
11
12
  return buffer;
12
- let j = buffer.length - 1;
13
+ const copy = Uint8Array.from(buffer);
14
+ let j = copy.length - 1;
13
15
  let tmp = 0;
14
- for (let i = 0; i < buffer.length / 2; i++) {
15
- tmp = buffer[i];
16
- buffer[i] = buffer[j];
17
- buffer[j] = tmp;
16
+ for (let i = 0; i < copy.length / 2; i++) {
17
+ tmp = copy[i];
18
+ copy[i] = copy[j];
19
+ copy[j] = tmp;
18
20
  j--;
19
21
  }
20
- return buffer;
21
- }
22
- function witnessStackToScriptWitness(witness) {
23
- let buffer = Buffer.allocUnsafe(0);
24
- function writeSlice(slice) {
25
- buffer = Buffer.concat([buffer, Buffer.from(slice)]);
26
- }
27
- function writeVarInt(i) {
28
- const currentLen = buffer.length;
29
- const varintLen = (0, varuint_bitcoin_1.encodingLength)(i);
30
- buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
31
- (0, varuint_bitcoin_1.encode)(i, buffer, currentLen);
32
- }
33
- function writeVarSlice(slice) {
34
- writeVarInt(slice.length);
35
- writeSlice(slice);
36
- }
37
- function writeVector(vector) {
38
- writeVarInt(vector.length);
39
- vector.forEach(writeVarSlice);
40
- }
41
- writeVector(witness);
42
- return buffer;
22
+ return copy;
43
23
  }
44
24
  function finalScriptsFuncFactory(scriptSatisfaction, network) {
45
25
  const finalScriptsFunc = (_index, _input, lockingScript /*witnessScript or redeemScript*/, isSegwit, isP2SH, _isP2WSH) => {
@@ -53,7 +33,7 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
53
33
  });
54
34
  if (!payment.witness)
55
35
  throw new Error(`Error: p2wsh failed producing a witness`);
56
- finalScriptWitness = witnessStackToScriptWitness(payment.witness);
36
+ finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(payment.witness);
57
37
  }
58
38
  //p2sh-p2wsh
59
39
  else if (isSegwit && isP2SH) {
@@ -66,7 +46,7 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
66
46
  });
67
47
  if (!payment.witness)
68
48
  throw new Error(`Error: p2sh-p2wsh failed producing a witness`);
69
- finalScriptWitness = witnessStackToScriptWitness(payment.witness);
49
+ finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(payment.witness);
70
50
  finalScriptSig = payment.input;
71
51
  }
72
52
  //p2sh
@@ -84,9 +64,14 @@ function finalScriptsFuncFactory(scriptSatisfaction, network) {
84
64
  return finalScriptsFunc;
85
65
  }
86
66
  /**
87
- * Important: Read comments on descriptor.updatePsbt regarding not passing txHex
67
+ * Important: Read comments on Output.updatePsbtAsInput regarding not passing txHex
88
68
  */
89
- function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, witnessScript, redeemScript, rbf }) {
69
+ function addPsbtInput({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, tapInternalKey, tapLeafScript, tapBip32Derivation, witnessScript, redeemScript, rbf }) {
70
+ if (value !== undefined && typeof value !== 'bigint')
71
+ throw new Error(`Error: value must be a bigint`);
72
+ if (value !== undefined && value < 0n)
73
+ throw new Error(`Error: value must be >= 0n`);
74
+ let normalizedValue = value;
90
75
  //Some data-sanity checks:
91
76
  if (sequence !== undefined && rbf && sequence > 0xfffffffd)
92
77
  throw new Error(`Error: incompatible sequence and rbf settings`);
@@ -104,7 +89,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
104
89
  const outputScript = out.script;
105
90
  if (!outputScript)
106
91
  throw new Error(`Error: could not extract outputScript for txHex ${txHex} and vout ${vout}`);
107
- if (Buffer.compare(outputScript, scriptPubKey) !== 0)
92
+ if ((0, uint8array_tools_1.compare)(outputScript, scriptPubKey) !== 0)
108
93
  throw new Error(`Error: txHex ${txHex} for vout ${vout} does not correspond to scriptPubKey ${scriptPubKey}`);
109
94
  if (txId !== undefined) {
110
95
  if (tx.getId() !== txId)
@@ -113,15 +98,15 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
113
98
  else {
114
99
  txId = tx.getId();
115
100
  }
116
- if (value !== undefined) {
117
- if (out.value !== value)
101
+ if (normalizedValue !== undefined) {
102
+ if (out.value !== normalizedValue)
118
103
  throw new Error(`Error: value for ${txHex} and vout ${vout} does not correspond to ${value}`);
119
104
  }
120
105
  else {
121
- value = out.value;
106
+ normalizedValue = out.value;
122
107
  }
123
108
  }
124
- if (txId === undefined || !value)
109
+ if (txId === undefined || normalizedValue === undefined)
125
110
  throw new Error(`Error: txHex+vout required. Alternatively, but ONLY for Segwit inputs, txId+value can also be passed.`);
126
111
  if (locktime) {
127
112
  if (psbt.locktime && psbt.locktime !== locktime)
@@ -149,7 +134,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
149
134
  }
150
135
  }
151
136
  const input = {
152
- hash: reverseBuffer(Buffer.from(txId, 'hex')),
137
+ hash: reverseBytes((0, uint8array_tools_1.fromHex)(txId)),
153
138
  index: vout
154
139
  };
155
140
  if (txHex !== undefined) {
@@ -157,7 +142,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
157
142
  }
158
143
  if (tapInternalKey) {
159
144
  //Taproot
160
- const tapBip32Derivation = keysInfo
145
+ const fallbackTapBip32Derivation = keysInfo
161
146
  .filter((keyInfo) => keyInfo.pubkey && keyInfo.masterFingerprint && keyInfo.path)
162
147
  .map((keyInfo) => {
163
148
  const pubkey = keyInfo.pubkey;
@@ -167,16 +152,15 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
167
152
  masterFingerprint: keyInfo.masterFingerprint,
168
153
  pubkey,
169
154
  path: keyInfo.path,
170
- leafHashes: [] // TODO: Empty array for tr(KEY) taproot key spend - this is the only type currently supported
155
+ leafHashes: []
171
156
  };
172
157
  });
173
- if (tapBip32Derivation.length)
174
- input.tapBip32Derivation = tapBip32Derivation;
158
+ const resolvedTapBip32Derivation = tapBip32Derivation || fallbackTapBip32Derivation;
159
+ if (resolvedTapBip32Derivation.length)
160
+ input.tapBip32Derivation = resolvedTapBip32Derivation;
175
161
  input.tapInternalKey = tapInternalKey;
176
- //TODO: currently only single-key taproot supported.
177
- //https://github.com/bitcoinjs/bitcoinjs-lib/blob/6ba8bb3ce20ba533eeaba6939cfc2891576d9969/test/integration/taproot.spec.ts#L243
178
- if (tapBip32Derivation.length > 1)
179
- throw new Error('Only single key taproot inputs are currently supported');
162
+ if (tapLeafScript && tapLeafScript.length > 0)
163
+ input.tapLeafScript = tapLeafScript;
180
164
  }
181
165
  else {
182
166
  const bip32Derivation = keysInfo
@@ -196,7 +180,7 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
196
180
  }
197
181
  if (isSegwit && txHex !== undefined) {
198
182
  //There's no need to put both witnessUtxo and nonWitnessUtxo
199
- input.witnessUtxo = { script: scriptPubKey, value };
183
+ input.witnessUtxo = { script: scriptPubKey, value: normalizedValue };
200
184
  }
201
185
  if (sequence !== undefined)
202
186
  input.sequence = sequence;
@@ -0,0 +1,25 @@
1
+ import type { Network } from 'bitcoinjs-lib';
2
+ export declare const MAX_SCRIPT_ELEMENT_SIZE = 520;
3
+ export declare const MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
4
+ export declare function assertScriptNonPushOnlyOpsLimit({ script }: {
5
+ script: Uint8Array;
6
+ }): void;
7
+ /**
8
+ * Enforces consensus stack resource limits.
9
+ */
10
+ export declare function assertConsensusStackResourceLimits({ stackItems, stackLabel, stackItemLabel }: {
11
+ stackItems: Uint8Array[];
12
+ stackLabel?: string;
13
+ stackItemLabel?: string;
14
+ }): void;
15
+ export declare function assertWitnessV0SatisfactionResourceLimits({ stackItems }: {
16
+ stackItems: Uint8Array[];
17
+ }): void;
18
+ export declare function assertTaprootScriptPathSatisfactionResourceLimits({ stackItems }: {
19
+ stackItems: Uint8Array[];
20
+ }): void;
21
+ export declare function assertP2shScriptSigStandardSize({ scriptSatisfaction, redeemScript, network }: {
22
+ scriptSatisfaction: Uint8Array;
23
+ redeemScript: Uint8Array;
24
+ network: Network;
25
+ }): void;