@scure/btc-signer 2.3.0 → 2.4.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.
package/src/utils.ts CHANGED
@@ -411,10 +411,32 @@ export function taprootTweakPubkey(pubKey: TArg<Bytes>, h: TArg<Bytes>): TRet<[B
411
411
  // This is the fixed BIP 341 H example, not the privacy-preserving H + rG variant.
412
412
  // Downstream helpers use exact-byte equality with it to recognize
413
413
  // library-generated script-only outputs.
414
- /** Standard unspendable internal key used for script-only Taproot outputs. */
415
- export const TAPROOT_UNSPENDABLE_KEY: TRet<Bytes> = /* @__PURE__ */ (() =>
414
+ // Keep the value used by library internals private: exported Uint8Arrays are mutable, so the
415
+ // public compatibility export below cannot safely be a source of cryptographic key material.
416
+ const INTERNAL_TAPROOT_NUMS: TRet<Bytes> = /* @__PURE__ */ (() =>
416
417
  sha256(Point.BASE.toBytes(false)) as TRet<Bytes>)();
417
418
 
419
+ /**
420
+ * Standard unspendable internal key used for script-only Taproot outputs.
421
+ * @deprecated Use {@link taprootNumsKey} to receive an owned copy.
422
+ */
423
+ export const TAPROOT_UNSPENDABLE_KEY: TRet<Bytes> = /* @__PURE__ */ (() =>
424
+ Uint8Array.from(INTERNAL_TAPROOT_NUMS) as TRet<Bytes>)();
425
+
426
+ /**
427
+ * Returns an owned copy of the library's stable Taproot NUMS key.
428
+ * @returns A new 32-byte NUMS key copy.
429
+ * @example
430
+ * Obtain an internal key without sharing mutable exported storage.
431
+ * ```ts
432
+ * import { taprootNumsKey } from '@scure/btc-signer/utils.js';
433
+ * const internalKey = taprootNumsKey();
434
+ * ```
435
+ */
436
+ export function taprootNumsKey(): TRet<Bytes> {
437
+ return Uint8Array.from(INTERNAL_TAPROOT_NUMS) as TRet<Bytes>;
438
+ }
439
+
418
440
  /** Bitcoin network parameters. */
419
441
  export type BTC_NETWORK = {
420
442
  /** Human-readable prefix used by Bech32 and Bech32m addresses. */
package/src/utxo.ts CHANGED
@@ -1,8 +1,15 @@
1
1
  import { hex } from '@scure/base';
2
2
  import * as P from 'micro-packed';
3
- import { Address, type CustomScript, OutScript, checkScript, tapLeafHash } from './payment.ts';
3
+ import {
4
+ Address,
5
+ type CustomScript,
6
+ OutScript,
7
+ _WitnessOutScript,
8
+ checkScript,
9
+ tapLeafHash,
10
+ } from './payment.ts';
4
11
  import * as psbt from './psbt.ts';
5
- import { CompactSizeLen, Script } from './script.ts';
12
+ import { CompactSizeLen, RawWitness, Script } from './script.ts';
6
13
  import {
7
14
  SignatureHash,
8
15
  Transaction,
@@ -20,12 +27,14 @@ import {
20
27
  type Bytes,
21
28
  NETWORK,
22
29
  PubT,
23
- TAPROOT_UNSPENDABLE_KEY,
24
30
  type TArg,
31
+ type TRet,
25
32
  compareBytes,
26
33
  equalBytes,
27
34
  isBytes,
28
35
  sha256,
36
+ taprootNumsKey,
37
+ taprootTweakPubkey,
29
38
  validatePubkey,
30
39
  validateObject,
31
40
  } from './utils.ts';
@@ -43,6 +52,7 @@ export type Accumulated =
43
52
  }
44
53
  | undefined;
45
54
  type TapLeafScript = psbt.TransactionInput['tapLeafScript'];
55
+ type TapLeaf = NonNullable<TapLeafScript>[number];
46
56
  type TB = Parameters<typeof psbt.TaprootControlBlock.encode>[0];
47
57
  const encodeTapBlock = (item: TB) => psbt.TaprootControlBlock.encode(item);
48
58
  // Be friendly to bad ECMAScript parsers by not using bigint literals.
@@ -51,70 +61,165 @@ const _0n = /* @__PURE__ */ BigInt(0), _3n = /* @__PURE__ */ BigInt(3);
51
61
  // Serialized length of VarBytes(data) without allocating the encoded copy
52
62
  const varLen = (dataLen: number) => CompactSizeLen.encode(dataLen).length + dataLen;
53
63
 
64
+ const tapLeafWitness = (
65
+ leaf: TArg<TapLeaf>,
66
+ sigSize: number,
67
+ customScripts?: TArg<CustomScript[]>,
68
+ pubkeys?: Set<string>,
69
+ unknownError = 'Finalize: Unknown tapLeafScript'
70
+ ): TRet<Bytes[] | undefined> => {
71
+ const [cb, _script] = leaf as TapLeaf;
72
+ const _customScripts = customScripts as CustomScript[] | undefined;
73
+ // Last byte is version
74
+ const script = _script.slice(0, -1);
75
+ const ver = _script[_script.length - 1];
76
+ const outs = OutScript.decode(script);
77
+ const available = (pubkey: TArg<Bytes>) => !pubkeys || pubkeys.has(hex.encode(pubkey as Bytes));
78
+ const empty = () => new Uint8Array(sigSize);
79
+ let signatures: Bytes[] = [];
80
+ if (outs.type === 'tr_ms') {
81
+ let added = 0;
82
+ for (const pubkey of outs.pubkeys) {
83
+ if (added === outs.m || !available(pubkey)) signatures.push(P.EMPTY);
84
+ else {
85
+ signatures.push(empty());
86
+ added++;
87
+ }
88
+ }
89
+ if (added !== outs.m) return;
90
+ } else if (outs.type === 'tr_ns') {
91
+ for (const pubkey of outs.pubkeys) {
92
+ if (!available(pubkey)) return;
93
+ signatures.push(empty());
94
+ }
95
+ } else {
96
+ if (!_customScripts) throw new Error(unknownError);
97
+ const leafHash = tapLeafHash(script, ver);
98
+ const scriptDecoded = Script.decode(script);
99
+ const scriptPubkeys = scriptDecoded.filter((i) => {
100
+ if (!isBytes(i)) return false;
101
+ try {
102
+ validatePubkey(i, PubT.schnorr);
103
+ return true;
104
+ } catch (e) {
105
+ return false;
106
+ }
107
+ }) as Bytes[];
108
+ const availablePubkeys = scriptPubkeys.filter(available);
109
+ // A custom finalizer may treat an unexpected empty signature list as malformed input. If the
110
+ // script embeds keys but none are owned, the path is unavailable without consulting the hook.
111
+ if (pubkeys && scriptPubkeys.length && !availablePubkeys.length) return;
112
+ let recognized = false;
113
+ for (const c of _customScripts) {
114
+ if (!c.finalizeTaproot) continue;
115
+ const csEncoded = c.encode(scriptDecoded);
116
+ if (csEncoded === undefined) continue;
117
+ recognized = true;
118
+ const finalized = c.finalizeTaproot(
119
+ script,
120
+ csEncoded,
121
+ availablePubkeys.map((pubKey) => [{ pubKey, leafHash }, empty()])
122
+ );
123
+ if (finalized) return finalized.concat(encodeTapBlock(cb)) as TRet<Bytes[]>;
124
+ }
125
+ if (recognized && pubkeys) return;
126
+ throw new Error(unknownError);
127
+ }
128
+ // Witness is stack, so last element will be used first
129
+ return signatures.reverse().concat([script, encodeTapBlock(cb)]) as TRet<Bytes[]>;
130
+ };
131
+
54
132
  function iterLeafs(
55
133
  tapLeafScript: TArg<TapLeafScript>,
56
134
  sigSize: number,
57
135
  customScripts?: TArg<CustomScript[]>
58
- ) {
136
+ ): TRet<Bytes[]> {
59
137
  const _tapLeafScript = tapLeafScript as TapLeafScript;
60
138
  const _customScripts = customScripts as CustomScript[] | undefined;
61
139
  if (!_tapLeafScript || !_tapLeafScript.length) throw new Error('no leafs');
62
- // Dummy non-empty Schnorr signature bytes for weight estimation.
63
- // Unsigned tr_ms slots use P.EMPTY below.
64
- const empty = () => new Uint8Array(sigSize);
65
- // If user want to select specific leaf, which can signed,
66
- // it is possible to remove all other leafs manually.
67
- // Sort leafs by control block length.
68
- const leafs = _tapLeafScript.sort(
69
- (a, b) => encodeTapBlock(a[0]).length - encodeTapBlock(b[0]).length
70
- );
71
- for (const [cb, _script] of leafs) {
72
- // Last byte is version
73
- const script = _script.slice(0, -1);
74
- const ver = _script[_script.length - 1];
75
- const outs = OutScript.decode(script);
140
+ // Start with the old shallowest-path order for stable equal-weight ties. Full witness size can
141
+ // reverse that order when a shallow leaf needs a larger script or more signatures.
142
+ const leafs = _tapLeafScript
143
+ .slice()
144
+ .sort((a, b) => encodeTapBlock(a[0]).length - encodeTapBlock(b[0]).length);
145
+ let smallest: Bytes[] | undefined;
146
+ let smallestSize = Number.POSITIVE_INFINITY;
147
+ for (const leaf of leafs) {
148
+ const witness = tapLeafWitness(leaf, sigSize, _customScripts);
149
+ if (!witness) continue;
150
+ const size = RawWitness.encode(witness).length;
151
+ if (size >= smallestSize) continue;
152
+ smallest = witness;
153
+ smallestSize = size;
154
+ }
155
+ if (!smallest) throw new Error('there was no witness');
156
+ return smallest as TRet<Bytes[]>;
157
+ }
76
158
 
77
- let signatures: Bytes[] = [];
78
- if (outs.type === 'tr_ms') {
79
- const m = outs.m;
80
- const n = outs.pubkeys.length - m;
81
- for (let i = 0; i < m; i++) signatures.push(empty());
82
- for (let i = 0; i < n; i++) signatures.push(P.EMPTY);
83
- } else if (outs.type === 'tr_ns') {
84
- for (const _pub of outs.pubkeys) signatures.push(empty());
85
- } else {
86
- if (!_customScripts) throw new Error('Finalize: Unknown tapLeafScript');
87
- const leafHash = tapLeafHash(script, ver);
88
- for (const c of _customScripts) {
89
- if (!c.finalizeTaproot) continue;
90
- const scriptDecoded = Script.decode(script);
91
- const csEncoded = c.encode(scriptDecoded);
92
- if (csEncoded === undefined) continue;
93
- const pubKeys = scriptDecoded.filter((i) => {
94
- if (!isBytes(i)) return false;
95
- try {
96
- validatePubkey(i, PubT.schnorr);
97
- return true;
98
- } catch (e) {
99
- return false;
100
- }
101
- }) as Bytes[];
102
- const finalized = c.finalizeTaproot(
103
- script,
104
- csEncoded,
105
- pubKeys.map((pubKey) => [{ pubKey, leafHash }, empty()])
106
- );
107
- if (!finalized) continue;
108
- return finalized.concat(encodeTapBlock(cb));
109
- }
110
- // UTXO selection may run without the real signer/finalizer process. When no matching local
111
- // finalizeTaproot hook exists, keep a minimal script-path witness lower bound here instead of
112
- // failing selection; callers that need exact fee estimates must provide the matching hook.
159
+ /**
160
+ * Removes Taproot spend paths that cannot be satisfied by the supplied Schnorr public keys.
161
+ * Non-Taproot inputs are retained, and caller-owned input metadata is never mutated.
162
+ * @param inputs - candidate PSBT input records to filter
163
+ * @param pubkeys - available x-only Schnorr public keys
164
+ * @returns Copies of inputs that retain at least one available path
165
+ * @example
166
+ * Filter wallet UTXOs before selecting coins.
167
+ * ```ts
168
+ * import { filterTaproot } from '@scure/btc-signer/utxo.js';
169
+ * import { pubSchnorr, randomPrivateKeyBytes } from '@scure/btc-signer/utils.js';
170
+ * const spendable = filterTaproot([], [pubSchnorr(randomPrivateKeyBytes())]);
171
+ * ```
172
+ */
173
+ export function filterTaproot(
174
+ inputs: TArg<psbt.TransactionInputUpdate[]>,
175
+ pubkeys: TArg<Bytes[]>
176
+ ): TRet<psbt.TransactionInputUpdate[]> {
177
+ aarray(inputs, 'inputs');
178
+ aarray(pubkeys, 'pubkeys');
179
+ const _inputs = inputs as psbt.TransactionInputUpdate[];
180
+ const _pubkeys = pubkeys as Bytes[];
181
+ const keys = new Set<string>();
182
+ for (const pubkey of _pubkeys) keys.add(hex.encode(validatePubkey(pubkey, PubT.schnorr)));
183
+ const res: psbt.TransactionInputUpdate[] = [];
184
+ for (const input of _inputs) {
185
+ const filtered = { ...input };
186
+ // BIP371 path fields are optional and empty keyed lists encode as absence, so the committed
187
+ // previous-output script is the authoritative input type.
188
+ const prevScript = _WitnessOutScript.decode(
189
+ getPrevOut(filtered as TArg<psbt.TransactionInput>).script
190
+ );
191
+ if (prevScript.type !== 'tr') {
192
+ res.push(filtered);
193
+ continue;
194
+ }
195
+ if (filtered.tapInternalKey) {
196
+ // A control block can reconstruct the root, but BIP371 does not require that inference.
197
+ // Omission may be broken data or an earlier filter deliberately disabling the key path.
198
+ // Recognize root omission only when the prevout proves this is an empty-tree commitment.
199
+ const hasRoot =
200
+ filtered.tapMerkleRoot !== undefined ||
201
+ equalBytes(taprootTweakPubkey(filtered.tapInternalKey, P.EMPTY)[0], prevScript.pubkey);
202
+ if (
203
+ !hasRoot ||
204
+ equalBytes(filtered.tapInternalKey, taprootNumsKey()) ||
205
+ (!keys.has(hex.encode(filtered.tapInternalKey)) && !keys.has(hex.encode(prevScript.pubkey)))
206
+ )
207
+ delete filtered.tapInternalKey;
113
208
  }
114
- // Witness is stack, so last element will be used first
115
- return signatures.reverse().concat([script, encodeTapBlock(cb)]);
209
+ if (filtered.tapLeafScript) {
210
+ const sigSize =
211
+ filtered.sighashType !== undefined && filtered.sighashType !== SignatureHash.DEFAULT
212
+ ? 65
213
+ : 64;
214
+ const leafs = filtered.tapLeafScript.filter((leaf) =>
215
+ tapLeafWitness(leaf, sigSize, undefined, keys, 'filterTaproot: unknown Taproot leaf')
216
+ );
217
+ if (leafs.length) filtered.tapLeafScript = leafs;
218
+ else delete filtered.tapLeafScript;
219
+ }
220
+ if (filtered.tapInternalKey || filtered.tapLeafScript) res.push(filtered);
116
221
  }
117
- throw new Error('there was no witness');
222
+ return res as TRet<psbt.TransactionInputUpdate[]>;
118
223
  }
119
224
 
120
225
  function estimateInput(
@@ -130,13 +235,9 @@ function estimateInput(
130
235
  // schnorr sig is always 64 bytes. except for cases when sighash is not default!
131
236
  if (inputType.txType === 'taproot') {
132
237
  const SCHNORR_SIG_SIZE = inputType.sighash !== SignatureHash.DEFAULT ? 65 : 64;
133
- // BIP371 `PSBT_IN_TAP_INTERNAL_KEY` is signer metadata, but UTXO selection
134
- // runs before signer availability is known. We intentionally treat a
135
- // present internal key as a key-path hint here to avoid overestimating fees
136
- // on the online side. Callers that know only script-path signing is
137
- // possible should omit `tapInternalKey` or pre-filter `tapLeafScript`
138
- // before estimation.
139
- if (_input.tapInternalKey && !equalBytes(_input.tapInternalKey, TAPROOT_UNSPENDABLE_KEY)) {
238
+ // A real internal key and every retained leaf declare paths available to this caller. Use
239
+ // filterTaproot before estimation when the supplied input contains unavailable paths.
240
+ if (_input.tapInternalKey && !equalBytes(_input.tapInternalKey, taprootNumsKey())) {
140
241
  witness = [new Uint8Array(SCHNORR_SIG_SIZE)];
141
242
  } else if (_input.tapLeafScript) {
142
243
  witness = iterLeafs(_input.tapLeafScript, SCHNORR_SIG_SIZE, _opts.customScripts);
@@ -219,6 +320,8 @@ export type EstimatorOpts = TxOpts & {
219
320
  createTx?: boolean; // Create tx inside selection
220
321
  requiredInputs?: psbt.TransactionInputUpdate[]; // these inputs always will be used
221
322
  allowSameUtxo?: boolean; // allow using UTXO multiple times (for test purposes)
323
+ /** Filter Taproot candidate paths to those satisfiable by these Schnorr public keys. */
324
+ filterTaproot?: Bytes[];
222
325
  };
223
326
 
224
327
  function getScript(o: TArg<Output>, opts: TArg<TxOpts> = {}, network = NETWORK) {
@@ -241,7 +344,7 @@ function getScript(o: TArg<Output>, opts: TArg<TxOpts> = {}, network = NETWORK)
241
344
  // Keep selector-only `createTx: false` flows aligned with the transaction/PSBT output boundary:
242
345
  // satoshi-denominated outputs are not allowed to go negative.
243
346
  abigint(_o.amount, 'output.amount');
244
- if (script && !_opts.allowUnknownOutputs && OutScript.decode(script).type === 'unknown') {
347
+ if (script && !_opts.allowUnknownOutputs && _WitnessOutScript.decode(script).type === 'unknown') {
245
348
  throw new Error(
246
349
  'Estimator: unknown output script type, there is a chance that input is unspendable. Pass allowUnknownOutputs=true, if you sure'
247
350
  );
@@ -258,10 +361,7 @@ type AccumStrategy = `accum${SortStrategy}`;
258
361
 
259
362
  /** Supported UTXO selection strategies. */
260
363
  export type SelectionStrategy =
261
- | 'all'
262
- | 'default'
263
- | AccumStrategy
264
- | `${ExactStrategy}/${AccumStrategy}`;
364
+ 'all' | 'default' | AccumStrategy | `${ExactStrategy}/${AccumStrategy}`;
265
365
 
266
366
  // class, because we need to re-use normalized inputs, instead of parsing each time
267
367
  // internal stuff, exported for tests only
@@ -284,6 +384,9 @@ export class _Estimator {
284
384
  private outputs: Output[];
285
385
  private opts: EstimatorOpts;
286
386
  constructor(inputs: psbt.TransactionInputUpdate[], outputs: Output[], opts: EstimatorOpts) {
387
+ // EstimatorOpts extends TxOpts, so resolve the complete transaction policy once even when
388
+ // createTx=false. This keeps selection normalization and a returned Transaction identical.
389
+ opts = new Transaction(opts).opts as EstimatorOpts;
287
390
  this.outputs = outputs;
288
391
  this.opts = opts;
289
392
  // Zero-fee estimation is useful on regtest/in tests, but negative fee rates would make
@@ -350,7 +453,8 @@ export class _Estimator {
350
453
  undefined,
351
454
  undefined,
352
455
  opts.disableScriptCheck,
353
- opts.allowUnknown
456
+ opts.unknown!,
457
+ opts.proprietary!
354
458
  );
355
459
  inputBeforeSign(normalized as TArg<psbt.TransactionInput>); // check fields
356
460
  const key = `${hex.encode(normalized.txid!)}:${normalized.index}`;
@@ -632,6 +736,16 @@ export function selectUTXO(
632
736
  astring(strategy, 'strategy');
633
737
  // Public wrapper defaults to BIP69 ordering and tx construction unless callers override them.
634
738
  const _opts = { createTx: true, bip69: true, ...(opts as EstimatorOpts) };
635
- const est = new _Estimator(inputs as psbt.TransactionInputUpdate[], outputs as Output[], _opts);
739
+ let candidates = inputs as psbt.TransactionInputUpdate[];
740
+ if (_opts.filterTaproot !== undefined) {
741
+ candidates = filterTaproot(candidates, _opts.filterTaproot);
742
+ if (_opts.requiredInputs) {
743
+ const requiredInputs = filterTaproot(_opts.requiredInputs, _opts.filterTaproot);
744
+ if (requiredInputs.length !== _opts.requiredInputs.length)
745
+ throw new Error('filterTaproot: required input has no available Taproot path');
746
+ _opts.requiredInputs = requiredInputs;
747
+ }
748
+ }
749
+ const est = new _Estimator(candidates, outputs as Output[], _opts);
636
750
  return est.result(strategy);
637
751
  }
package/transaction.d.ts CHANGED
@@ -74,6 +74,8 @@ export declare const def: <T>(value: T | undefined, def: T) => T;
74
74
  * ```
75
75
  */
76
76
  export declare function cloneDeep<T>(obj: T): T;
77
+ /** PSBT unknown/proprietary-field handling policy. */
78
+ export type Unknowns = psbt.Unknowns;
77
79
  /** Transaction construction and parsing options. */
78
80
  export interface TxOpts {
79
81
  /** Transaction version to place into new transactions and imported PSBTs. */
@@ -98,17 +100,36 @@ export interface TxOpts {
98
100
  allowUnknowInput?: boolean;
99
101
  /** Allow signing and finalizing inputs with unknown script shapes. */
100
102
  allowUnknownInputs?: boolean;
101
- /** Skip redeem-script and witness-script consistency checks. */
103
+ /** Skip redeem/witness-script and Taproot commitment consistency checks. */
102
104
  disableScriptCheck?: boolean;
103
105
  /** Match the odd empty-output encoding used by `bip174js`. */
104
106
  bip174jsCompat?: boolean;
105
107
  /** Permit legacy inputs that only provide witness UTXO data. */
106
108
  allowLegacyWitnessUtxo?: boolean;
109
+ /**
110
+ * Before signing, require every input to provide a full previous transaction whose txid and
111
+ * selected output match the input. Use this for untrusted or multi-party PSBTs to prevent forged
112
+ * witness-UTXO amounts from hiding an excessive transaction fee.
113
+ */
114
+ strictPrevoutValidation?: boolean;
107
115
  /** Grind ECDSA signatures until they use a low-R encoding. */
108
116
  lowR?: boolean;
109
117
  /** UNSAFE: additional custom payment-script codecs and finalizers. */
110
118
  customScripts?: CustomScript[];
111
- /** Preserve unknown PSBT key/value pairs instead of stripping them. */
119
+ /** Unknown PSBT field policy. Defaults to `strip`. */
120
+ unknown?: Unknowns;
121
+ /** Proprietary PSBT field policy. Defaults to the resolved {@link unknown} policy. */
122
+ proprietary?: Unknowns;
123
+ /**
124
+ * Treat an absent PSBTv2 transaction-modifiable field as allowing input/output changes. Older
125
+ * scure versions emitted PSBTv2 without this field, so this opts out of strict BIP370 behavior
126
+ * when upgrading and editing their PSBTs.
127
+ */
128
+ allowMissingTxModifiable?: boolean;
129
+ /**
130
+ * Deprecated alias for {@link unknown}: true selects `ignore`, false selects `strip`.
131
+ * @deprecated Use `unknown`.
132
+ */
112
133
  allowUnknown?: boolean;
113
134
  }
114
135
  /**
@@ -200,9 +221,12 @@ export declare function getPrevOut(input: TArg<psbt.TransactionInput>): P.Unwrap
200
221
  * @param i - input update to normalize
201
222
  * @param cur - existing input value to merge with
202
223
  * @param allowedFields - fields that may still change on signed inputs
203
- * @param disableScriptCheck - whether to skip redeem/witness script sanity checks
204
- * @param allowUnknown - whether to keep unknown PSBT fields
224
+ * @param disableScriptCheck - whether to skip wrapper and Taproot commitment sanity checks
225
+ * @param unknown - handling policy for unknown PSBT fields
226
+ * @param proprietary - handling policy for proprietary PSBT fields
205
227
  * @returns Normalized PSBT input.
228
+ * @throws If the update conflicts with the existing input or its signatures. {@link Error}
229
+ * @throws If a numeric input field is outside its wire or protocol range. {@link RangeError}
206
230
  * @example
207
231
  * Accept hex txids from callers in the same display-order form used by `Transaction.id`, then
208
232
  * normalize them into the repo's internal `TransactionInput` shape.
@@ -216,7 +240,7 @@ export declare function getPrevOut(input: TArg<psbt.TransactionInput>): P.Unwrap
216
240
  * });
217
241
  * ```
218
242
  */
219
- export declare function normalizeInput(i: TArg<psbt.TransactionInputUpdate>, cur?: TArg<PSBTInputs>, allowedFields?: TArg<readonly (keyof PSBTInputs)[]>, disableScriptCheck?: boolean, allowUnknown?: boolean): TRet<PSBTInputs>;
243
+ export declare function normalizeInput(i: TArg<psbt.TransactionInputUpdate>, cur?: TArg<PSBTInputs>, allowedFields?: TArg<readonly (keyof PSBTInputs)[]>, disableScriptCheck?: boolean, unknown?: Unknowns | boolean, proprietary?: Unknowns | boolean): TRet<PSBTInputs>;
220
244
  /**
221
245
  * Determines how an input should be signed and finalized.
222
246
  * Wrapper consistency is expected to be validated earlier by {@link normalizeInput}
@@ -305,13 +329,21 @@ export declare class Transaction {
305
329
  private outputs;
306
330
  readonly opts: ReturnType<typeof validateOpts>;
307
331
  constructor(opts?: TxOpts);
332
+ private isPSBTv2;
333
+ private requireTxModifiable;
334
+ private txModifiablePolicy;
335
+ private modifiable;
336
+ private get txModifiable();
308
337
  static fromRaw(raw: Bytes, opts?: TxOpts): Transaction;
309
338
  static fromPSBT(psbt_: Bytes, opts?: TxOpts): Transaction;
310
339
  toPSBT(PSBTVersion?: number | undefined): Uint8Array;
311
340
  get lockTime(): number;
312
341
  get version(): number;
313
342
  private inputStatus;
343
+ private cleanFinalInput;
314
344
  private inputSighash;
345
+ private signatures;
346
+ private signedInputKeys;
315
347
  private signStatus;
316
348
  get isFinal(): boolean;
317
349
  get hasWitnesses(): boolean;
@@ -323,6 +355,7 @@ export declare class Transaction {
323
355
  get hash(): string;
324
356
  get id(): string;
325
357
  private checkInputIdx;
358
+ private validatePrevoutsForSigning;
326
359
  getInput(idx: number): psbt.TransactionInput;
327
360
  get inputsLength(): number;
328
361
  addInput(input: TArg<psbt.TransactionInputUpdate>, _ignoreSignStatus?: boolean): number;
@@ -350,6 +383,7 @@ export declare class Transaction {
350
383
  /**
351
384
  * Merges multiple PSBT blobs into one.
352
385
  * @param psbts - PSBT byte arrays to combine
386
+ * @param opts - Transaction parsing, combination, and serialization options. See {@link TxOpts}.
353
387
  * @returns Combined PSBT bytes.
354
388
  * @throws If the PSBT list is empty or the partial transactions cannot be combined. {@link Error}
355
389
  * @example
@@ -360,7 +394,7 @@ export declare class Transaction {
360
394
  * PSBTCombine([psbt, psbt]);
361
395
  * ```
362
396
  */
363
- export declare function PSBTCombine(psbts: TArg<Bytes[]>): TRet<Bytes>;
397
+ export declare function PSBTCombine(psbts: TArg<Bytes[]>, opts?: TArg<TxOpts>): TRet<Bytes>;
364
398
  /**
365
399
  * Parses a BIP32 path string into child indices.
366
400
  * @param path - derivation path such as `m/0'/1`