@bitcoinerlab/descriptors 2.3.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.trLedger = exports.wpkhLedger = exports.shWpkhLedger = exports.pkhLedger = exports.trBIP32 = exports.wpkhBIP32 = exports.shWpkhBIP32 = exports.pkhBIP32 = void 0;
4
4
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
5
5
  const keyExpressions_1 = require("./keyExpressions");
6
+ const networkUtils_1 = require("./networkUtils");
6
7
  function assertStandardKeyPath(keyPath) {
7
8
  // Regular expression to match "/change/index" or "/change/*" format
8
9
  const regex = /^\/[01]\/(\d+|\*)$/;
@@ -25,7 +26,7 @@ function standardExpressionsBIP32Maker(purpose, scriptTemplate) {
25
26
  * - `{change:0, index:'*'}`.
26
27
  */
27
28
  function standardScriptExpressionBIP32({ masterNode, network = bitcoinjs_lib_1.networks.bitcoin, keyPath, account, change, index, isPublic = true }) {
28
- const originPath = `/${purpose}'/${network === bitcoinjs_lib_1.networks.bitcoin ? 0 : 1}'/${account}'`;
29
+ const originPath = `/${purpose}'/${(0, networkUtils_1.coinTypeFromNetwork)(network)}'/${account}'`;
29
30
  if (keyPath !== undefined)
30
31
  assertStandardKeyPath(keyPath);
31
32
  const keyExpression = (0, keyExpressions_1.keyExpressionBIP32)({
@@ -49,24 +50,26 @@ exports.wpkhBIP32 = standardExpressionsBIP32Maker(84, 'wpkh(KEYEXPRESSION)');
49
50
  /** @function */
50
51
  exports.trBIP32 = standardExpressionsBIP32Maker(86, 'tr(KEYEXPRESSION)');
51
52
  function standardExpressionsLedgerMaker(purpose, scriptTemplate) {
52
- /** @overload */
53
- async function standardScriptExpressionLedger({ ledgerClient, ledgerState, ledgerManager, network, account, keyPath, change, index }) {
54
- if (ledgerManager && (ledgerClient || ledgerState))
55
- throw new Error(`ledgerClient and ledgerState have been deprecated`);
56
- if (ledgerManager && network)
57
- throw new Error(`ledgerManager already includes the network object`);
58
- if (!ledgerManager && !network)
59
- network = bitcoinjs_lib_1.networks.bitcoin;
60
- if (ledgerManager)
61
- ({ ledgerClient, ledgerState, network } = ledgerManager);
62
- if (!ledgerClient || !ledgerState)
63
- throw new Error(`Could not retrieve ledgerClient or ledgerState`);
64
- const originPath = `/${purpose}'/${network === bitcoinjs_lib_1.networks.bitcoin ? 0 : 1}'/${account}'`;
53
+ /**
54
+ * Computes the standard descriptor based on given parameters.
55
+ *
56
+ * You can define the output location either by:
57
+ * - Providing the full `keyPath` (e.g., "/0/2").
58
+ * OR
59
+ * - Specifying the `change` and `index` values separately (e.g., `{change:0, index:2}`).
60
+ *
61
+ * For ranged indexing, the `index` can be set as a wildcard '*'. For example:
62
+ * - `keyPath="/0/*"`
63
+ * OR
64
+ * - `{change:0, index:'*'}`.
65
+ */
66
+ async function standardScriptExpressionLedger({ ledgerManager, account, keyPath, change, index }) {
67
+ const { network } = ledgerManager;
68
+ const originPath = `/${purpose}'/${(0, networkUtils_1.coinTypeFromNetwork)(network)}'/${account}'`;
65
69
  if (keyPath !== undefined)
66
70
  assertStandardKeyPath(keyPath);
67
71
  const keyExpression = await (0, keyExpressions_1.keyExpressionLedger)({
68
- ledgerClient,
69
- ledgerState,
72
+ ledgerManager,
70
73
  originPath,
71
74
  keyPath,
72
75
  change,
package/dist/signers.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { Psbt } from 'bitcoinjs-lib';
2
2
  import type { ECPairInterface } from 'ecpair';
3
3
  import type { BIP32Interface } from 'bip32';
4
- import type { DescriptorInstance } from './descriptors';
5
- import { LedgerState, LedgerManager } from './ledger';
4
+ import { LedgerManager } from './ledger';
6
5
  /**
7
6
  * Signs a specific input of a PSBT with an ECPair.
8
7
  *
@@ -69,16 +68,6 @@ export declare function signInputLedger({ psbt, index, ledgerManager }: {
69
68
  index: number;
70
69
  ledgerManager: LedgerManager;
71
70
  }): Promise<void>;
72
- /**
73
- * @hidden
74
- */
75
- export declare function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }: {
76
- psbt: Psbt;
77
- index: number;
78
- descriptor: DescriptorInstance;
79
- ledgerClient: unknown;
80
- ledgerState: LedgerState;
81
- }): Promise<void>;
82
71
  /**
83
72
  * Signs the inputs of the `psbt` where the keys are controlled by a Ledger
84
73
  * device.
@@ -93,12 +82,3 @@ export declare function signLedger({ psbt, ledgerManager }: {
93
82
  psbt: Psbt;
94
83
  ledgerManager: LedgerManager;
95
84
  }): Promise<void>;
96
- /**
97
- * @hidden
98
- */
99
- export declare function signLedger({ psbt, descriptors, ledgerClient, ledgerState }: {
100
- psbt: Psbt;
101
- descriptors: DescriptorInstance[];
102
- ledgerClient: unknown;
103
- ledgerState: LedgerState;
104
- }): Promise<void>;
package/dist/signers.js CHANGED
@@ -8,8 +8,7 @@ exports.signInputBIP32 = signInputBIP32;
8
8
  exports.signBIP32 = signBIP32;
9
9
  exports.signInputLedger = signInputLedger;
10
10
  exports.signLedger = signLedger;
11
- const bip371_1 = require("bitcoinjs-lib/src/psbt/bip371");
12
- const bip341_1 = require("bitcoinjs-lib/src/payments/bip341");
11
+ const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
13
12
  const ledger_1 = require("./ledger");
14
13
  const applyPR2137_1 = require("./applyPR2137");
15
14
  function range(n) {
@@ -39,12 +38,12 @@ function signInputECPair({ psbt, index, ecpair }) {
39
38
  const input = psbt.data.inputs[index];
40
39
  if (!input)
41
40
  throw new Error('Invalid index');
42
- if ((0, bip371_1.isTaprootInput)(input)) {
41
+ if ((0, bitcoinjs_lib_internals_1.isTaprootInput)(input)) {
43
42
  // If script-path (tapLeafScript present) -> DO NOT TWEAK
44
43
  if (input.tapLeafScript && input.tapLeafScript.length > 0)
45
44
  psbt.signInput(index, ecpair);
46
45
  else {
47
- const hash = (0, bip341_1.tapTweakHash)(Buffer.from(ecpair.publicKey.slice(1, 33)), undefined);
46
+ const hash = (0, bitcoinjs_lib_internals_1.tapTweakHash)(ecpair.publicKey.slice(1, 33), undefined);
48
47
  const tweakedEcpair = ecpair.tweak(hash);
49
48
  psbt.signInput(index, tweakedEcpair);
50
49
  }
@@ -101,136 +100,91 @@ function signBIP32({ psbt, masterNode }) {
101
100
  }
102
101
  const ledgerSignaturesForInputIndex = (index, ledgerSignatures) => ledgerSignatures
103
102
  .filter(([i]) => i === index)
104
- .map(([_i, partialSignature]) => ({
105
- pubkey: partialSignature.pubkey,
106
- signature: partialSignature.signature
107
- }));
108
- /**
109
- * To be removed in v3.0 and replaced by a version that does not accept
110
- * descriptor
111
- * @overload
112
- */
113
- async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState, ledgerManager }) {
114
- if (!descriptor && !ledgerManager)
115
- throw new Error(`ledgerManager not provided`);
116
- if (descriptor && ledgerManager)
117
- throw new Error(`Invalid usage: don't pass descriptor`);
118
- if (ledgerManager && (ledgerClient || ledgerState))
119
- throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
120
- const output = descriptor;
121
- if (ledgerManager)
122
- ({ ledgerClient, ledgerState } = ledgerManager);
123
- if (!ledgerClient)
124
- throw new Error(`ledgerManager not provided`);
125
- if (!ledgerState)
126
- throw new Error(`ledgerManager not provided`);
127
- const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
128
- if (!(ledgerClient instanceof AppClient))
129
- throw new Error(`Error: pass a valid ledgerClient`);
130
- let ledgerSignatures;
131
- if (ledgerManager) {
132
- if (psbt.data.inputs[index]?.tapInternalKey)
133
- throw new Error('Taproot inputs not yet supported for the Ledger device');
134
- const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
135
- psbt,
136
- index,
137
- ledgerManager
138
- });
139
- if (!policy)
140
- throw new Error(`Error: the ledger cannot sign this pstb input`);
141
- if (policy.policyName && policy.policyHmac && policy.policyId) {
142
- //non-standard policy
143
- const walletPolicy = new WalletPolicy(policy.policyName, policy.ledgerTemplate, policy.keyRoots);
144
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
103
+ .map(([_i, partialSignature]) => partialSignature);
104
+ function addLedgerSignaturesToInput({ psbt, index, ledgerSignatures }) {
105
+ const input = psbt.data.inputs[index];
106
+ if (!input)
107
+ throw new Error(`Error: input ${index} not available`);
108
+ const signatures = ledgerSignaturesForInputIndex(index, ledgerSignatures);
109
+ if (signatures.length === 0)
110
+ throw new Error(`Error: no ledger signatures found for input ${index}`);
111
+ if ((0, bitcoinjs_lib_internals_1.isTaprootInput)(input)) {
112
+ // Ledger returns per-input signatures as [pubkey, signature, tapleafHash?].
113
+ // For taproot we must map them to PSBT taproot fields (not partialSig):
114
+ // - signatures with tapleafHash -> tapScriptSig[] (script-path)
115
+ // - signature without tapleafHash -> tapKeySig (key-path)
116
+ // A taproot input may contain script-path signatures, key-path signature,
117
+ // or both in edge cases; each must be written to its corresponding field.
118
+ const tapScriptSig = signatures
119
+ .filter((sig) => sig.tapleafHash)
120
+ .map((sig) => ({
121
+ pubkey: sig.pubkey,
122
+ signature: sig.signature,
123
+ leafHash: sig.tapleafHash
124
+ }));
125
+ const tapKeySigs = signatures.filter((sig) => !sig.tapleafHash);
126
+ if (tapScriptSig.length > 0) {
127
+ psbt.updateInput(index, { tapScriptSig });
145
128
  }
146
- else {
147
- //standard policy
148
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(policy.ledgerTemplate, policy.keyRoots[0]), null);
129
+ if (tapKeySigs.length > 1)
130
+ throw new Error(`Error: expected at most one tapKeySig for input ${index}`);
131
+ const tapKeySig = tapKeySigs[0]?.signature;
132
+ if (tapKeySig) {
133
+ psbt.updateInput(index, { tapKeySig });
149
134
  }
135
+ if (tapScriptSig.length === 0 && !tapKeySig)
136
+ throw new Error(`Error: no valid taproot ledger signatures found for input ${index}`);
150
137
  }
151
138
  else {
152
- if (!output)
153
- throw new Error(`outputs not provided`);
154
- const result = await (0, ledger_1.ledgerPolicyFromOutput)({
155
- output,
156
- ledgerClient,
157
- ledgerState
158
- });
159
- if (!result)
160
- throw new Error(`Error: output does not have a ledger input`);
161
- const { ledgerTemplate, keyRoots } = result;
162
- const standardPolicy = await (0, ledger_1.ledgerPolicyFromStandard)({
163
- output,
164
- ledgerClient,
165
- ledgerState
166
- });
167
- if (standardPolicy) {
168
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
169
- }
170
- else {
171
- const policy = await (0, ledger_1.ledgerPolicyFromState)({
172
- output,
173
- ledgerClient,
174
- ledgerState
175
- });
176
- if (!policy || !policy.policyName || !policy.policyHmac)
177
- throw new Error(`Error: the descriptor's policy is not registered`);
178
- const walletPolicy = new WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
179
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
180
- }
139
+ const partialSig = signatures.map((sig) => ({
140
+ pubkey: sig.pubkey,
141
+ signature: sig.signature
142
+ }));
143
+ psbt.updateInput(index, { partialSig });
181
144
  }
182
- //Add the signatures to the Psbt object using PartialSig format:
183
- psbt.updateInput(index, {
184
- partialSig: ledgerSignaturesForInputIndex(index, ledgerSignatures)
145
+ }
146
+ async function signInputLedger({ psbt, index, ledgerManager }) {
147
+ const { ledgerClient } = ledgerManager;
148
+ const { DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
149
+ if (!(ledgerClient instanceof AppClient))
150
+ throw new Error(`Error: pass a valid ledgerClient`);
151
+ const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
152
+ psbt,
153
+ index,
154
+ ledgerManager
185
155
  });
156
+ if (!policy)
157
+ throw new Error(`Error: the ledger cannot sign this pstb input`);
158
+ let ledgerSignatures;
159
+ if (policy.policyName && policy.policyHmac && policy.policyId) {
160
+ //non-standard policy
161
+ const walletPolicy = new WalletPolicy(policy.policyName, policy.ledgerTemplate, policy.keyRoots);
162
+ const walletHmac = policy.policyHmac;
163
+ ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), walletPolicy, walletHmac);
164
+ }
165
+ else {
166
+ //standard policy
167
+ ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), new DefaultWalletPolicy(policy.ledgerTemplate, policy.keyRoots[0]), null);
168
+ }
169
+ addLedgerSignaturesToInput({ psbt, index, ledgerSignatures });
186
170
  }
187
- /**
188
- * To be removed in v3.0 and replaced by a version that does not accept
189
- * descriptors
190
- * @overload
191
- */
192
- async function signLedger({ psbt, descriptors, ledgerClient, ledgerState, ledgerManager }) {
193
- if (!descriptors && !ledgerManager)
194
- throw new Error(`ledgerManager not provided`);
195
- if (descriptors && ledgerManager)
196
- throw new Error(`Invalid usage: don't pass descriptors`);
197
- if (ledgerManager && (ledgerClient || ledgerState))
198
- throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
199
- const outputs = descriptors;
200
- if (ledgerManager)
201
- ({ ledgerClient, ledgerState } = ledgerManager);
202
- if (!ledgerClient)
203
- throw new Error(`ledgerManager not provided`);
204
- if (!ledgerState)
205
- throw new Error(`ledgerManager not provided`);
206
- const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
171
+ async function signLedger({ psbt, ledgerManager }) {
172
+ const { ledgerClient } = ledgerManager;
173
+ const { DefaultWalletPolicy, WalletPolicy, AppClient } = (await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient));
207
174
  if (!(ledgerClient instanceof AppClient))
208
175
  throw new Error(`Error: pass a valid ledgerClient`);
209
176
  const ledgerPolicies = [];
210
- if (ledgerManager)
211
- for (let index = 0; index < psbt.data.inputs.length; index++) {
212
- if (psbt.data.inputs[index]?.tapInternalKey)
213
- throw new Error('Taproot inputs not yet supported for the Ledger device');
214
- const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
215
- psbt,
216
- index,
217
- ledgerManager
218
- });
219
- if (policy)
220
- ledgerPolicies.push(policy);
221
- }
222
- else {
223
- if (!outputs)
224
- throw new Error(`outputs not provided`);
225
- for (const output of outputs) {
226
- const policy = (await (0, ledger_1.ledgerPolicyFromState)({ output, ledgerClient, ledgerState })) ||
227
- (await (0, ledger_1.ledgerPolicyFromStandard)({ output, ledgerClient, ledgerState }));
228
- if (policy)
229
- ledgerPolicies.push(policy);
230
- }
231
- if (ledgerPolicies.length === 0)
232
- throw new Error(`Error: there are no inputs which could be signed`);
177
+ for (let index = 0; index < psbt.data.inputs.length; index++) {
178
+ const policy = await (0, ledger_1.ledgerPolicyFromPsbtInput)({
179
+ psbt,
180
+ index,
181
+ ledgerManager
182
+ });
183
+ if (policy)
184
+ ledgerPolicies.push(policy);
233
185
  }
186
+ if (ledgerPolicies.length === 0)
187
+ throw new Error(`Error: there are no inputs which could be signed`);
234
188
  //cluster unique LedgerPolicies
235
189
  const uniquePolicies = [];
236
190
  for (const policy of ledgerPolicies) {
@@ -244,16 +198,18 @@ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState, ledger
244
198
  uniquePolicy.policyId) {
245
199
  //non-standard policy
246
200
  const walletPolicy = new WalletPolicy(uniquePolicy.policyName, uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots);
247
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, uniquePolicy.policyHmac);
201
+ const walletHmac = uniquePolicy.policyHmac;
202
+ ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), walletPolicy, walletHmac);
248
203
  }
249
204
  else {
250
205
  //standard policy
251
- ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
206
+ ledgerSignatures = await ledgerClient.signPsbt(psbt.toBase64(), new DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
252
207
  }
253
- for (const [index, ,] of ledgerSignatures) {
254
- psbt.updateInput(index, {
255
- partialSig: ledgerSignaturesForInputIndex(index, ledgerSignatures)
256
- });
208
+ const signedIndexes = [
209
+ ...new Set(ledgerSignatures.map(([index]) => index))
210
+ ];
211
+ for (const index of signedIndexes) {
212
+ addLedgerSignaturesToInput({ psbt, index, ledgerSignatures });
257
213
  }
258
214
  }
259
215
  }
@@ -0,0 +1,17 @@
1
+ export declare const MAX_STACK_SIZE = 1000;
2
+ export declare const MAX_SCRIPT_ELEMENT_SIZE = 520;
3
+ export declare function assertConsensusStackResourceLimits({ stackItems, stackLabel, stackItemLabel }: {
4
+ stackItems: Uint8Array[];
5
+ stackLabel?: string;
6
+ stackItemLabel?: string;
7
+ }): void;
8
+ export declare function assertStandardPolicyStackItemCountLimit({ stackItems, maxStackItems, stackLabel }: {
9
+ stackItems: Uint8Array[];
10
+ maxStackItems: number;
11
+ stackLabel: string;
12
+ }): void;
13
+ export declare function assertStandardPolicyStackItemSizeLimit({ stackItems, maxStackItemSize, stackItemLabel }: {
14
+ stackItems: Uint8Array[];
15
+ maxStackItemSize: number;
16
+ stackItemLabel: string;
17
+ }): void;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Jose-Luis Landabaso
3
+ // Distributed under the MIT software license
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.MAX_SCRIPT_ELEMENT_SIZE = exports.MAX_STACK_SIZE = void 0;
6
+ exports.assertConsensusStackResourceLimits = assertConsensusStackResourceLimits;
7
+ exports.assertStandardPolicyStackItemCountLimit = assertStandardPolicyStackItemCountLimit;
8
+ exports.assertStandardPolicyStackItemSizeLimit = assertStandardPolicyStackItemSizeLimit;
9
+ // See Sipa's Miniscript "Resource limitations":
10
+ // https://bitcoin.sipa.be/miniscript/
11
+ // and Bitcoin Core policy/consensus constants.
12
+ // Consensus: max number of elements in initial stack (and stack+altstack after
13
+ // each opcode execution).
14
+ exports.MAX_STACK_SIZE = 1000;
15
+ // Consensus: max size for any pushed stack element. This is an element limit,
16
+ // not a full script-size limit.
17
+ exports.MAX_SCRIPT_ELEMENT_SIZE = 520;
18
+ function assertConsensusStackResourceLimits({ stackItems, stackLabel = 'stack', stackItemLabel = 'stack item' }) {
19
+ if (stackItems.length > exports.MAX_STACK_SIZE)
20
+ throw new Error(`Error: ${stackLabel} has too many items, ${stackItems.length} is larger than ${exports.MAX_STACK_SIZE}`);
21
+ for (const stackItem of stackItems) {
22
+ if (stackItem.length > exports.MAX_SCRIPT_ELEMENT_SIZE)
23
+ throw new Error(`Error: ${stackItemLabel} is too large, ${stackItem.length} bytes is larger than ${exports.MAX_SCRIPT_ELEMENT_SIZE} bytes`);
24
+ }
25
+ }
26
+ function assertStandardPolicyStackItemCountLimit({ stackItems, maxStackItems, stackLabel }) {
27
+ if (stackItems.length > maxStackItems)
28
+ throw new Error(`Error: ${stackLabel} has too many items, ${stackItems.length} is larger than ${maxStackItems}`);
29
+ }
30
+ function assertStandardPolicyStackItemSizeLimit({ stackItems, maxStackItemSize, stackItemLabel }) {
31
+ for (const stackItem of stackItems) {
32
+ if (stackItem.length > maxStackItemSize)
33
+ throw new Error(`Error: ${stackItemLabel} exceeds standard policy, ${stackItem.length} bytes is larger than ${maxStackItemSize} bytes`);
34
+ }
35
+ }
@@ -0,0 +1,193 @@
1
+ import { Network } from 'bitcoinjs-lib';
2
+ import type { BIP32API } from 'bip32';
3
+ import type { ECPairAPI } from 'ecpair';
4
+ import type { PartialSig, TapBip32Derivation } from 'bip174';
5
+ import type { Taptree } from 'bitcoinjs-lib/src/cjs/types';
6
+ import type { KeyInfo, Preimage, TimeConstraints } from './types';
7
+ import type { TapLeafInfo, TapTreeInfoNode, TapTreeNode } from './tapTree';
8
+ export type TaprootLeafSatisfaction = {
9
+ leaf: TapLeafInfo;
10
+ depth: number;
11
+ tapLeafHash: Uint8Array;
12
+ scriptSatisfaction: Uint8Array;
13
+ stackItems: Uint8Array[];
14
+ nLockTime: number | undefined;
15
+ nSequence: number | undefined;
16
+ totalWitnessSize: number;
17
+ };
18
+ export type TaprootPsbtLeafMetadata = {
19
+ leaf: TapLeafInfo;
20
+ depth: number;
21
+ tapLeafHash: Uint8Array;
22
+ controlBlock: Uint8Array;
23
+ };
24
+ /**
25
+ * Compiles a taproot miniscript tree into per-leaf metadata.
26
+ * Each leaf contains its expanded miniscript, expansion map, compiled tapscript
27
+ * and leaf version. This keeps the taproot script-path data ready for
28
+ * satisfactions and witness building.
29
+ */
30
+ export declare function buildTapTreeInfo({ tapTree, network, BIP32, ECPair }: {
31
+ tapTree: TapTreeNode;
32
+ network?: Network;
33
+ BIP32: BIP32API;
34
+ ECPair: ECPairAPI;
35
+ }): TapTreeInfoNode;
36
+ export declare function tapTreeInfoToScriptTree(tapTreeInfo: TapTreeInfoNode): Taptree;
37
+ /**
38
+ * Builds taproot PSBT leaf metadata for every leaf in a `tapTreeInfo`.
39
+ *
40
+ * For each leaf, this function computes:
41
+ * - `tapLeafHash`: BIP341 leaf hash of tapscript + leaf version
42
+ * - `depth`: leaf depth in the tree (root children have depth 1)
43
+ * - `controlBlock`: script-path proof used in PSBT `tapLeafScript`
44
+ *
45
+ * The control block layout is:
46
+ *
47
+ * ```text
48
+ * [1-byte (leafVersion | parity)] [32-byte internal key]
49
+ * [32-byte sibling hash #1] ... [32-byte sibling hash #N]
50
+ * ```
51
+ *
52
+ * where:
53
+ * - `parity` is derived from tweaking the internal key with the tree root
54
+ * - sibling hashes are the merkle path from that leaf to the root
55
+ *
56
+ * Example tree:
57
+ *
58
+ * ```text
59
+ * root
60
+ * / \
61
+ * L1 L2
62
+ * / \
63
+ * L3 L4
64
+ * ```
65
+ *
66
+ * Depths:
67
+ * - L1 depth = 1
68
+ * - L3 depth = 2
69
+ * - L4 depth = 2
70
+ *
71
+ * Conceptual output:
72
+ *
73
+ * ```text
74
+ * [
75
+ * L1 -> { depth: 1, tapLeafHash: h1, controlBlock: [v|p, ik, hash(L2)] }
76
+ * L3 -> { depth: 2, tapLeafHash: h3, controlBlock: [v|p, ik, hash(L4), hash(L1)] }
77
+ * L4 -> { depth: 2, tapLeafHash: h4, controlBlock: [v|p, ik, hash(L3), hash(L1)] }
78
+ * ]
79
+ * ```
80
+ *
81
+ * Legend:
82
+ * - `ik`: the 32-byte internal key placed in the control block.
83
+ * - `hash(X)`: the merkle sibling hash at each level when proving leaf `X`.
84
+ *
85
+ * Note: in this diagram, `L2` is a branch node (right subtree), not a leaf,
86
+ * so `hash(L2) = TapBranch(hash(L3), hash(L4))`.
87
+ *
88
+ * Notes:
89
+ * - Leaves are returned in deterministic left-first order.
90
+ * - One metadata entry is returned per leaf.
91
+ * - `controlBlock.length === 33 + 32 * depth`.
92
+ * - Throws if internal key is invalid or merkle path cannot be found.
93
+ *
94
+ * Typical usage:
95
+ * - Convert this metadata into PSBT `tapLeafScript[]` entries
96
+ * for all leaves.
97
+ */
98
+ export declare function buildTaprootLeafPsbtMetadata({ tapTreeInfo, internalPubkey }: {
99
+ tapTreeInfo: TapTreeInfoNode;
100
+ internalPubkey: Uint8Array;
101
+ }): TaprootPsbtLeafMetadata[];
102
+ /**
103
+ * Builds PSBT `tapBip32Derivation` entries for taproot script-path spends.
104
+ *
105
+ * Leaf keys include the list of tapleaf hashes where they appear.
106
+ * If `internalKeyInfo` has derivation data, it is included with empty
107
+ * `leafHashes`.
108
+ *
109
+ * Example tree:
110
+ *
111
+ * ```text
112
+ * root
113
+ * / \
114
+ * L1 L2
115
+ *
116
+ * L1 uses key A
117
+ * L2 uses key A and key B
118
+ *
119
+ * h1 = tapleafHash(L1)
120
+ * h2 = tapleafHash(L2)
121
+ * ```
122
+ *
123
+ * Then output is conceptually:
124
+ *
125
+ * ```text
126
+ * [
127
+ * key A -> leafHashes [h1, h2]
128
+ * key B -> leafHashes [h2]
129
+ * internal key -> leafHashes []
130
+ * ]
131
+ * ```
132
+ *
133
+ * Notes:
134
+ * - Keys missing `masterFingerprint` or `path` are skipped.
135
+ * - Duplicate pubkeys are merged.
136
+ * - If the same pubkey appears with conflicting derivation metadata,
137
+ * this function throws.
138
+ * - Output and `leafHashes` are sorted deterministically.
139
+ */
140
+ export declare function buildTaprootBip32Derivations({ tapTreeInfo, internalKeyInfo }: {
141
+ tapTreeInfo: TapTreeInfoNode;
142
+ internalKeyInfo?: KeyInfo;
143
+ }): TapBip32Derivation[];
144
+ export declare function normalizeTaprootPubkey(pubkey: Uint8Array): Uint8Array;
145
+ /**
146
+ * Computes satisfactions for taproot script-path leaves.
147
+ *
148
+ * If `tapLeaf` is undefined, all satisfiable leaves are returned. If `tapLeaf`
149
+ * is provided, only that leaf is considered.
150
+ *
151
+ * Callers are expected to pass real signatures, or fake signatures generated
152
+ * during planning. See satisfyMiniscript() for how timeConstraints keep the
153
+ * chosen leaf consistent between planning and signing.
154
+ */
155
+ export declare function collectTaprootLeafSatisfactions({ tapTreeInfo, preimages, signatures, timeConstraints, tapLeaf }: {
156
+ tapTreeInfo: TapTreeInfoNode;
157
+ preimages: Preimage[];
158
+ signatures: PartialSig[];
159
+ timeConstraints?: TimeConstraints;
160
+ tapLeaf?: Uint8Array | string;
161
+ }): TaprootLeafSatisfaction[];
162
+ /**
163
+ * Selects the taproot leaf satisfaction with the smallest total witness size.
164
+ * Assumes the input list is in left-first tree order for deterministic ties.
165
+ */
166
+ export declare function selectBestTaprootLeafSatisfaction(satisfactions: TaprootLeafSatisfaction[]): TaprootLeafSatisfaction;
167
+ /**
168
+ * Collects a unique set of taproot leaf pubkeys (x-only) across the tree.
169
+ * This is useful for building fake signatures when no signer subset is given.
170
+ */
171
+ export declare function collectTapTreePubkeys(tapTreeInfo: TapTreeInfoNode): Uint8Array[];
172
+ /**
173
+ * Returns the best satisfaction for a taproot tree, by witness size.
174
+ *
175
+ * If `tapLeaf` is provided, only that leaf is considered. If `tapLeaf` is a
176
+ * bytes, it is treated as a tapLeafHash and must match exactly one leaf. If
177
+ * `tapLeaf` is a string, it is treated as a miniscript leaf and must match
178
+ * exactly one leaf (whitespace-insensitive).
179
+ *
180
+ * This function is typically called twice:
181
+ * 1) Planning pass: call it with fake signatures (built by the caller) to
182
+ * choose the best leaf without requiring user signatures.
183
+ * 2) Signing pass: call it again with real signatures and the timeConstraints
184
+ * returned from the first pass (see satisfyMiniscript() for why this keeps
185
+ * the chosen leaf consistent between planning and signing).
186
+ */
187
+ export declare function satisfyTapTree({ tapTreeInfo, signatures, preimages, tapLeaf, timeConstraints }: {
188
+ tapTreeInfo: TapTreeInfoNode;
189
+ signatures: PartialSig[];
190
+ preimages: Preimage[];
191
+ tapLeaf?: Uint8Array | string;
192
+ timeConstraints?: TimeConstraints;
193
+ }): TaprootLeafSatisfaction;