@scure/btc-signer 2.3.0 → 2.4.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 CHANGED
@@ -515,13 +515,28 @@ deepStrictEqual(hex.encode(btc.OutScript.encode(decoded)), '51024e73');
515
515
 
516
516
  ### Encode/decode
517
517
 
518
- We support both PSBTv0 and draft PSBTv2 (there is no PSBTv1). If PSBTv2 transaction is encoded into PSBTv1, all PSBTv2 fields will be stripped.
518
+ We support both PSBTv0 and PSBTv2 (there is no PSBTv1). Explicit PSBTv0 conversion removes
519
+ PSBTv2-only fields after translating the unsigned transaction representation.
519
520
 
520
- We strip 'unknown' keys inside PSBT, they needed for new version/features support,
521
- however any unsupported feature/new version can significantly break assumptions about code.
522
- If you have use-case where they are needed, create a github issue.
521
+ Assigned fields from BIPs 322, 353, 372, 373, 375, and 376 are decoded explicitly and survive the
522
+ default path. `TxOpts.unknown` and `TxOpts.proprietary` govern only genuinely unknown and
523
+ proprietary records:
523
524
 
524
- PSBTv2 features tx_modifiable and taproot+bip32 are not supported yet.
525
+ - `ignore` preserves them, but the library may operate without understanding future constraints.
526
+ - `strip` accepts and removes them; this is the default and can break interoperability with future
527
+ extensions.
528
+ - `strict` rejects them.
529
+
530
+ Undefined bits in PSBTv2 `txModifiable` follow the `unknown` policy. `strip` and `strict` can also
531
+ fingerprint this library or version, while `ignore` offers better forwarding compatibility at the
532
+ cost of interpreting less protocol state. The deprecated `allowUnknown` boolean maps `true` to
533
+ `ignore` and `false` to `strip`.
534
+
535
+ When signing an untrusted or multi-party PSBT, pass
536
+ `{ strictPrevoutValidation: true }` to `Transaction.fromPSBT`. This requires every input to include
537
+ a full `nonWitnessUtxo` matching its outpoint before any signature is produced. It prevents forged
538
+ `witnessUtxo` amounts from making the displayed transaction fee appear lower than the fee that the
539
+ valid transaction will actually pay.
525
540
 
526
541
  ```text
527
542
  // Decode
@@ -937,10 +952,28 @@ when making an on-chain bitcoin payment. The library:
937
952
  - calculates weight with good precision
938
953
  - implements multiple strategies
939
954
 
940
- Taproot estimation is precise, but you have to pass sighash if you want to use non-default one,
941
- because it changes signature size. For complex taproot trees you need to filter tapLeafScript
942
- to include only leafs which you can sign we estimate size with smallest leaf (same as finalization),
943
- but in specific case keys for this leaf can be unavailable (complex multisig)
955
+ Taproot estimation is precise, but you have to pass `sighashType` when using a non-default
956
+ sighash because it changes signature size. A Taproot input can advertise paths that the current
957
+ wallet cannot spend. Pass the wallet's x-only public keys as `filterTaproot` to remove unavailable
958
+ internal-key and script paths before selection:
959
+
960
+ ```js
961
+ import * as btc from '@scure/btc-signer';
962
+
963
+ const select = (utxos, outputs, changeAddress, feePerByte, myPublicKey) =>
964
+ btc.selectUTXO(utxos, outputs, 'default', {
965
+ changeAddress,
966
+ feePerByte,
967
+ filterTaproot: [myPublicKey],
968
+ });
969
+ ```
970
+
971
+ Filtering is opt-in: when `filterTaproot` is omitted, no paths are removed. The standalone
972
+ `btc.filterTaproot(utxos, [myPublicKey])` utility returns the same filtered candidate records.
973
+ Built-in `tr_ns` and `tr_ms` paths are checked directly. Custom or unknown leaves are rejected;
974
+ callers must filter those paths using their own semantics before invoking this helper. Estimation
975
+ and finalization choose the available path with the smallest complete witness, retaining the old
976
+ shallowest-path order for equal weights.
944
977
 
945
978
  `Oldest` / `Newest` expects UTXO provided in historical order (oldest first),
946
979
  otherwise we have no way to detect age of tx.
@@ -1148,7 +1181,7 @@ const fetcher = ftch(fetch, {
1148
1181
  timeout: 10_000,
1149
1182
  concurrencyLimit: 4,
1150
1183
  });
1151
- const net = new EsploraProvider(fetcher, 'http://127.0.0.1:3000')
1184
+ const net = new EsploraProvider(fetcher, 'http://127.0.0.1:3000');
1152
1185
  ```
1153
1186
 
1154
1187
  ## MuSig2
@@ -1156,6 +1189,12 @@ const net = new EsploraProvider(fetcher, 'http://127.0.0.1:3000')
1156
1189
  MuSig2 implementation conforming to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki)
1157
1190
  is available in `@scure/btc-signer/musig2.js`. Check out [bip327-musig2.test.ts](./test/bip327-musig2.test.ts) as well:
1158
1191
 
1192
+ **Security warning:** every MuSig2 secret nonce must be used for exactly one partial signature.
1193
+ `Session.sign()` zeroes only the exact `Uint8Array` passed to it; clones, serialized values,
1194
+ database records, and process snapshots remain live. Reusing the same nonce in distinct sessions
1195
+ can reveal the signer's secret key. Stateful signers must keep one authoritative nonce record and
1196
+ atomically consume it before releasing a partial signature.
1197
+
1159
1198
  > `npm install @noble/curves`
1160
1199
 
1161
1200
  ```ts
package/index.d.ts CHANGED
@@ -5,9 +5,10 @@ export { multisig, p2ms, p2pk, p2pkh, p2sh, p2tr, p2tr_ms, p2tr_ns, p2tr_pk, p2w
5
5
  export { CompactSize, MAX_SCRIPT_BYTE_LENGTH, OP, RawTx, RawWitness, Script, ScriptNum, } from './script.ts';
6
6
  export type { ScriptType } from './script.ts';
7
7
  export { getInputType, Transaction } from './transaction.ts';
8
- export { NETWORK, TAPROOT_UNSPENDABLE_KEY, TEST_NETWORK } from './utils.ts';
8
+ export type { TxOpts, Unknowns } from './transaction.ts';
9
+ export { NETWORK, TAPROOT_UNSPENDABLE_KEY, TEST_NETWORK, taprootNumsKey } from './utils.ts';
9
10
  export type { TArg, TRet } from './utils.ts';
10
- export { selectUTXO } from './utxo.ts';
11
+ export { filterTaproot, selectUTXO } from './utxo.ts';
11
12
  /**
12
13
  * Small collection of commonly used utility exports.
13
14
  * @example
@@ -24,7 +25,7 @@ export declare const utils: TRet<Readonly<{
24
25
  randomPrivateKeyBytes: () => TRet<Uint8Array>;
25
26
  taprootTweakPubkey: typeof taprootTweakPubkey;
26
27
  }>>;
27
- export { _sortPubkeys, Address, combinations, getAddress, OutScript, sortedMultisig, taprootListToTree, WIF, } from './payment.ts';
28
+ export { _sortPubkeys, Address, combinations, getAddress, MAX_COMBINATIONS, OutScript, sortedMultisig, taprootListToTree, WIF, } from './payment.ts';
28
29
  export type { CustomScript, OptScript } from './payment.ts';
29
30
  export { _DebugPSBT, TaprootControlBlock } from './psbt.ts';
30
31
  export { bip32Path, Decimal, DEFAULT_SEQUENCE, PSBTCombine, SigHash } from './transaction.ts';
package/index.js CHANGED
@@ -4,8 +4,8 @@ import { compareBytes, concatBytes, isBytes, pubSchnorr, randomPrivateKeyBytes,
4
4
  export { multisig, p2ms, p2pk, p2pkh, p2sh, p2tr, p2tr_ms, p2tr_ns, p2tr_pk, p2wpkh, p2wsh } from "./payment.js";
5
5
  export { CompactSize, MAX_SCRIPT_BYTE_LENGTH, OP, RawTx, RawWitness, Script, ScriptNum, } from "./script.js";
6
6
  export { getInputType, Transaction } from "./transaction.js";
7
- export { NETWORK, TAPROOT_UNSPENDABLE_KEY, TEST_NETWORK } from "./utils.js";
8
- export { selectUTXO } from "./utxo.js";
7
+ export { NETWORK, TAPROOT_UNSPENDABLE_KEY, TEST_NETWORK, taprootNumsKey } from "./utils.js";
8
+ export { filterTaproot, selectUTXO } from "./utxo.js";
9
9
  /**
10
10
  * Small collection of commonly used utility exports.
11
11
  * @example
@@ -23,7 +23,7 @@ export const utils = /* @__PURE__ */ (() => Object.freeze({
23
23
  randomPrivateKeyBytes,
24
24
  taprootTweakPubkey,
25
25
  }))();
26
- export { _sortPubkeys, Address, combinations, getAddress, OutScript, sortedMultisig, taprootListToTree, WIF, } from "./payment.js";
26
+ export { _sortPubkeys, Address, combinations, getAddress, MAX_COMBINATIONS, OutScript, sortedMultisig, taprootListToTree, WIF, } from "./payment.js";
27
27
  // remove
28
28
  export { _DebugPSBT, TaprootControlBlock } from "./psbt.js";
29
29
  // remove
package/musig2.d.ts CHANGED
@@ -4,7 +4,10 @@ import { type TArg, type TRet } from './utils.ts';
4
4
  export type Nonces = {
5
5
  /** Public nonce that gets shared with the other participants. */
6
6
  public: Uint8Array;
7
- /** Secret nonce that stays local until partial signing finishes. */
7
+ /**
8
+ * Secret nonce that stays local until partial signing finishes. It MUST be consumed exactly once;
9
+ * never retain a copy that could be loaded for another signing session.
10
+ */
8
11
  secret: Uint8Array;
9
12
  };
10
13
  /**
@@ -116,6 +119,12 @@ export declare function keyAggregate(publicKeys: TArg<Uint8Array[]>, tweaks?: TA
116
119
  export declare function keyAggExport(ctx: ReturnType<typeof keyAggregate>): TRet<Uint8Array>;
117
120
  /**
118
121
  * Generates a nonce pair (public and secret) for MuSig2 signing.
122
+ *
123
+ * SECURITY: The returned secret nonce MUST be used for exactly one partial signature. Keep one
124
+ * authoritative copy and atomically consume it when calling {@link Session.sign}. That method
125
+ * zeroes only the exact `Uint8Array` passed to it; clones, serialized values, database records, and
126
+ * snapshots remain live. Reusing a secret nonce in distinct sessions can reveal the secret key.
127
+ *
119
128
  * @param publicKey - individual public key of the signer
120
129
  * @param secretKey - optional secret key, mixed in to blind the randomness source
121
130
  * @param aggPublicKey - aggregate public key of all signers
@@ -227,7 +236,12 @@ export declare class Session {
227
236
  /**
228
237
  * Generates a partial signature for a given message, secret nonce,
229
238
  * secret key, and session context.
230
- * @param secretNonce - secret nonce for this signing session; it is zeroed after use
239
+ *
240
+ * SECURITY: `secretNonce` MUST be used exactly once. This method zeroes the first 64 bytes of the
241
+ * supplied array, including when later validation fails, but cannot erase copies or persisted
242
+ * representations. Reusing those nonce scalars in a distinct session can reveal the secret key.
243
+ *
244
+ * @param secretNonce - sole authoritative secret-nonce buffer for this signing session
231
245
  * @param secret - secret key of the signer
232
246
  * @param fastSign - if `true`, skip the self-verification pass
233
247
  * @returns The partial signature (Uint8Array).
@@ -247,7 +261,7 @@ export declare class Session {
247
261
  partialSigVerify(partialSig: Uint8Array, pubNonces: Uint8Array[], i: number): boolean;
248
262
  /**
249
263
  * Aggregates partial signatures from multiple signers into a single final signature.
250
- * @param partialSigs - partial signatures from each signer
264
+ * @param partialSigs - exactly one positional partial signature per session participant
251
265
  * @returns The final aggregate signature (Uint8Array).
252
266
  * @throws If the input is invalid, such as wrong array sizes or malformed
253
267
  * signatures. {@link Error}
package/musig2.js CHANGED
@@ -288,6 +288,12 @@ const nonceHash = (rand, publicKey, aggPublicKey, i, msgPrefixed, extraIn) =>
288
288
  taggedInt('MuSig/nonce', rand, new Uint8Array([publicKey.length]), publicKey, new Uint8Array([aggPublicKey.length]), aggPublicKey, msgPrefixed, numberToBytesBE(extraIn.length, 4), extraIn, new Uint8Array([i]));
289
289
  /**
290
290
  * Generates a nonce pair (public and secret) for MuSig2 signing.
291
+ *
292
+ * SECURITY: The returned secret nonce MUST be used for exactly one partial signature. Keep one
293
+ * authoritative copy and atomically consume it when calling {@link Session.sign}. That method
294
+ * zeroes only the exact `Uint8Array` passed to it; clones, serialized values, database records, and
295
+ * snapshots remain live. Reusing a secret nonce in distinct sessions can reveal the secret key.
296
+ *
291
297
  * @param publicKey - individual public key of the signer
292
298
  * @param secretKey - optional secret key, mixed in to blind the randomness source
293
299
  * @param aggPublicKey - aggregate public key of all signers
@@ -505,7 +511,12 @@ export class Session {
505
511
  /**
506
512
  * Generates a partial signature for a given message, secret nonce,
507
513
  * secret key, and session context.
508
- * @param secretNonce - secret nonce for this signing session; it is zeroed after use
514
+ *
515
+ * SECURITY: `secretNonce` MUST be used exactly once. This method zeroes the first 64 bytes of the
516
+ * supplied array, including when later validation fails, but cannot erase copies or persisted
517
+ * representations. Reusing those nonce scalars in a distinct session can reveal the secret key.
518
+ *
519
+ * @param secretNonce - sole authoritative secret-nonce buffer for this signing session
509
520
  * @param secret - secret key of the signer
510
521
  * @param fastSign - if `true`, skip the self-verification pass
511
522
  * @returns The partial signature (Uint8Array).
@@ -587,17 +598,18 @@ export class Session {
587
598
  }
588
599
  /**
589
600
  * Aggregates partial signatures from multiple signers into a single final signature.
590
- * @param partialSigs - partial signatures from each signer
601
+ * @param partialSigs - exactly one positional partial signature per session participant
591
602
  * @returns The final aggregate signature (Uint8Array).
592
603
  * @throws If the input is invalid, such as wrong array sizes or malformed
593
604
  * signatures. {@link Error}
594
605
  */
595
606
  partialSigAgg(partialSigs) {
596
607
  abytesArray(partialSigs, 32);
597
- // BIP327 PartialSigAgg is defined for a non-empty psig_1..u list tied to this session_ctx;
598
- // [] is not a valid aggregate-signature input even though the sum starts from zero.
599
- if (partialSigs.length < 1)
600
- throw new RangeError('partialSigs.length must be >= 1');
608
+ // BIP327 PartialSigAgg consumes psig_1..u for the same u signers in session_ctx. Accepting
609
+ // fewer or more scalars would return a signature-shaped value for a different equation.
610
+ if (partialSigs.length !== this.publicKeys.length)
611
+ throw new RangeError(`partialSigs.length=${partialSigs.length} must equal ` +
612
+ `participant count=${this.publicKeys.length}`);
601
613
  const { Q, tweakAcc, R, e } = this;
602
614
  let s = _0n;
603
615
  for (let i = 0; i < partialSigs.length; i++) {
package/net.js CHANGED
@@ -724,8 +724,13 @@ export class EsploraProvider {
724
724
  const remaining = options.timeoutMs === undefined ? pollIntervalMs : options.timeoutMs - (Date.now() - start);
725
725
  if (remaining <= 0)
726
726
  throw new EsploraError('waitForTx: timeout');
727
- // Cap the sleep so the next loop checks the deadline before another poll.
728
- await sleep(Math.min(pollIntervalMs, remaining), options.signal);
727
+ if (options.timeoutMs !== undefined && remaining <= pollIntervalMs) {
728
+ // Sleeping the rest of the window reaches the deadline; the timer may
729
+ // wake before Date.now() agrees, so don't re-poll on a clock check.
730
+ await sleep(remaining, options.signal);
731
+ throw new EsploraError('waitForTx: timeout');
732
+ }
733
+ await sleep(pollIntervalMs, options.signal);
729
734
  }
730
735
  }
731
736
  async txInfo(txid) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scure/btc-signer",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Audited & minimal library for Bitcoin. Handle transactions, Schnorr, Taproot, UTXO & PSBT",
5
5
  "files": [
6
6
  "*.js",
@@ -9,20 +9,21 @@
9
9
  "!_type_test.*"
10
10
  ],
11
11
  "dependencies": {
12
- "@noble/curves": "~2.3.0",
13
- "@noble/hashes": "~2.3.0",
14
- "@scure/base": "~2.3.0",
15
- "micro-packed": "~0.11.0"
12
+ "@noble/curves": "2.4.0",
13
+ "@noble/hashes": "2.4.0",
14
+ "@scure/base": "2.4.0",
15
+ "micro-packed": "0.11.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@paulmillr/jsbt": "0.6.5",
19
- "@scure/bip32": "2.2.0",
20
- "micro-ftch": "^1.1.0",
21
- "prettier": "3.6.2",
22
- "typescript": "6.0.2"
18
+ "@paulmillr/jsbt": "0.7.1",
19
+ "bismar": "0.1.8",
20
+ "@scure/bip32": "2.4.0",
21
+ "micro-ftch": "1.2.0",
22
+ "prettier": "3.9.6",
23
+ "typescript": "6.0.3"
23
24
  },
24
25
  "scripts": {
25
- "benchmark:size": "npx bismar@0.1.3 -s",
26
+ "benchmark:size": "bismar -bsm",
26
27
  "build": "tsc",
27
28
  "build:clean": "rm -f *.{js,d.ts}",
28
29
  "check": "jsbt-check",
package/payment.d.ts CHANGED
@@ -91,6 +91,7 @@ export type CustomScript = Coder<OptScript, CustomScriptOut | undefined> & {
91
91
  export declare const OutScript: TRet<P.CoderType<NonNullable<OutP2AType | OutPKType | OutPKHType | OutSHType | OutWSHType | OutWPKHType | OutMSType | OutTRType | OutTRNSType | OutTRMSType | OutUnknownType | undefined>>>;
92
92
  /** Type of the output-script coder. */
93
93
  export type OutScriptType = typeof OutScript;
94
+ export declare const _WitnessOutScript: OutScriptType;
94
95
  type AddressValue = NonNullable<ReturnType<OutScriptType['decode']>>;
95
96
  /**
96
97
  * Validates that nested redeem and witness scripts match their wrappers.
@@ -186,6 +187,7 @@ export type P2SHReturn<T extends P2Ret> = T extends {
186
187
  * Wraps a child script inside P2SH.
187
188
  * @param child - child payment descriptor to wrap
188
189
  * @param network - address network parameters
190
+ * @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
189
191
  * @returns P2SH descriptor preserving witness metadata when present.
190
192
  * @throws If the wrapped script combination is invalid or unsupported. {@link Error}
191
193
  * @example
@@ -196,7 +198,7 @@ export type P2SHReturn<T extends P2Ret> = T extends {
196
198
  * p2sh(p2wsh(p2pk(hex.decode('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'))));
197
199
  * ```
198
200
  */
199
- export declare const p2sh: <T extends P2Ret>(child: TArg<T>, network?: BTC_NETWORK) => TRet<Extends<P2SHReturn<T>, P2Ret>>;
201
+ export declare const p2sh: <T extends P2Ret>(child: TArg<T>, network?: BTC_NETWORK, allowNonCanonicalScript?: boolean) => TRet<Extends<P2SHReturn<T>, P2Ret>>;
200
202
  /** Pay-to-witness-script-hash descriptor. */
201
203
  export type P2WSH = {
202
204
  /** Payment-script tag for pay-to-witness-script-hash outputs. */
@@ -214,6 +216,7 @@ export type P2WSH = {
214
216
  * Wraps a child script inside native SegWit P2WSH.
215
217
  * @param child - child payment descriptor to wrap
216
218
  * @param network - address network parameters
219
+ * @param allowNonCanonicalScript - whether to create an address for a non-minimal child script
217
220
  * @returns P2WSH descriptor.
218
221
  * @throws If the wrapped script combination is invalid or unsupported. {@link Error}
219
222
  * @example
@@ -224,7 +227,7 @@ export type P2WSH = {
224
227
  * p2wsh(p2pk(hex.decode('0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')));
225
228
  * ```
226
229
  */
227
- export declare const p2wsh: (child: TArg<P2Ret>, network?: BTC_NETWORK) => TRet<Extends<P2WSH, P2Ret>>;
230
+ export declare const p2wsh: (child: TArg<P2Ret>, network?: BTC_NETWORK, allowNonCanonicalScript?: boolean) => TRet<Extends<P2WSH, P2Ret>>;
228
231
  /** Pay-to-witness-public-key-hash descriptor. */
229
232
  export type P2WPKH = {
230
233
  /** Payment-script tag for pay-to-witness-public-key-hash outputs. */
@@ -380,6 +383,7 @@ export type P2TRRet<T> = T extends TaprootScriptTree ? P2TR_TREE : P2TR;
380
383
  * @param customScripts - optional custom script codecs for taproot leaves
381
384
  * @returns Taproot descriptor with optional script-path metadata.
382
385
  * @throws If the internal key or taproot script tree is invalid. {@link Error}
386
+ * @throws If a numeric script value is outside its supported range. {@link RangeError}
383
387
  * @example
384
388
  * Combine script leaves into a final taproot output descriptor and address.
385
389
  * ```ts
@@ -393,23 +397,27 @@ export type P2TRRet<T> = T extends TaprootScriptTree ? P2TR_TREE : P2TR;
393
397
  */
394
398
  export declare function p2tr(internalPubKey: TArg<Bytes | string>, tree?: undefined, network?: BTC_NETWORK, allowUnknownOutputs?: boolean, customScripts?: TArg<CustomScript[]>): TRet<Extends<P2TR, P2Ret>>;
395
399
  export declare function p2tr(internalPubKey: TArg<Bytes | string | undefined>, tree: TArg<TaprootScriptTree>, network?: BTC_NETWORK, allowUnknownOutputs?: boolean, customScripts?: TArg<CustomScript[]>): TRet<Extends<P2TR_TREE, P2Ret>>;
400
+ /** Maximum number of combinations materialized by one default helper call. */
401
+ export declare const MAX_COMBINATIONS = 4096;
396
402
  /**
397
403
  * Returns all size-`m` combinations from a list.
398
404
  * @param m - size of each combination
399
405
  * @param list - input items to combine
406
+ * @param maxCombinations - maximum result rows to materialize
400
407
  * @returns Array of combinations.
401
408
  * @throws If the combination size or input list is invalid. {@link Error}
409
+ * @throws If the requested result exceeds the materialization limit. {@link RangeError}
402
410
  * @example
403
411
  * Enumerate all size-two subsets of a short list.
404
412
  * ```ts
405
413
  * combinations(2, [1, 2, 3]);
406
414
  * ```
407
415
  */
408
- export declare function combinations<T>(m: number, list: T[]): T[][];
416
+ export declare function combinations<T>(m: number, list: T[], maxCombinations?: number): T[][];
409
417
  /**
410
418
  * M-of-N multi-leaf wallet via p2tr_ns. If m == n, single script is emitted.
411
- * Takes O(n^2) if m != n. 99-of-100 is ok, 5-of-100 is not.
412
- * It materializes C(n, m) leaves, so middle-of-the-range thresholds blow up combinatorially.
419
+ * It materializes C(n, m) leaves up to {@link MAX_COMBINATIONS}; middle-of-the-range thresholds
420
+ * above that bound are rejected before allocation.
413
421
  * `2-of-[A,B,C] => [A,B] | [A,C] | [B,C]`
414
422
  */
415
423
  export type P2TR_NS = {
@@ -425,6 +433,7 @@ export type P2TR_NS = {
425
433
  * @param allowSamePubkeys - whether duplicate keys are allowed
426
434
  * @returns Array of taproot leaf descriptors.
427
435
  * @throws If the taproot multisig parameters are invalid. {@link Error}
436
+ * @throws If the requested leaf set exceeds the materialization limit. {@link RangeError}
428
437
  * @example
429
438
  * Build the leaf set for an M-of-N taproot `CHECKSIGVERIFY` policy.
430
439
  * ```ts
@@ -443,6 +452,7 @@ export type P2TR_PK = P2TR_NS;
443
452
  * @param pubkey - Schnorr public key
444
453
  * @returns Taproot single-key leaf descriptor.
445
454
  * @throws If the taproot single-key leaf cannot be encoded. {@link Error}
455
+ * @throws If the delegated leaf policy exceeds its supported range. {@link RangeError}
446
456
  * @example
447
457
  * Build a single-key tapscript leaf.
448
458
  * ```ts
@@ -482,6 +492,7 @@ export declare function p2tr_ms(m: number, pubkeys: TArg<Bytes[]>, allowSamePubk
482
492
  * @param network - address network parameters
483
493
  * @returns Encoded Bitcoin address.
484
494
  * @throws If the requested address type is unknown. {@link Error}
495
+ * @throws If a key-derived script value is outside its supported range. {@link RangeError}
485
496
  * @example
486
497
  * Pick the output type first, then derive the matching address from the private key.
487
498
  * ```ts