@scure/btc-signer 2.2.0 → 2.3.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/transaction.js CHANGED
@@ -1,13 +1,18 @@
1
1
  import { hex } from '@scure/base';
2
+ import { anumber } from '@noble/hashes/utils.js';
2
3
  import * as P from 'micro-packed';
3
4
  import { Address, OutScript, checkScript, tapLeafHash } from "./payment.js";
4
5
  import * as psbt from "./psbt.js";
5
- import { CompactSizeLen, OP, RawOldTx, RawInput, RawOutput, RawTx, RawWitness, Script, scriptPushLen, VarBytes, } from "./script.js";
6
+ import { CompactSizeLen, OP, RawOldTx, RawInput, RawOutput, RawTx, Script, scriptPushLen, VarBytes, } from "./script.js";
6
7
  import * as u from "./utils.js";
7
- import { NETWORK, concatBytes, equalBytes, isBytes, } from "./utils.js";
8
+ import { NETWORK, abigint, concatBytes, equalBytes, isBytes, validateObject, } from "./utils.js";
9
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
10
+ // prettier-ignore
11
+ const _0n = /* @__PURE__ */ BigInt(0), _1n = /* @__PURE__ */ BigInt(1);
12
+ const U64_MAX = /* @__PURE__ */ BigInt('0xffffffffffffffff');
8
13
  const EMPTY32 = /* @__PURE__ */ new Uint8Array(32);
9
14
  const EMPTY_OUTPUT = {
10
- amount: 0xffffffffffffffffn,
15
+ amount: U64_MAX,
11
16
  script: P.EMPTY,
12
17
  };
13
18
  /**
@@ -195,6 +200,7 @@ function outputBeforeSign(i) {
195
200
  * ```
196
201
  */
197
202
  export function inputBeforeSign(i) {
203
+ validateObject(i, {}, {}, 'i');
198
204
  if (i.txid === undefined || i.index === undefined)
199
205
  throw new Error('Transaction/input: txid and index required');
200
206
  const res = {
@@ -235,8 +241,8 @@ function unpackSighash(hashType) {
235
241
  };
236
242
  }
237
243
  function validateOpts(opts) {
238
- if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')
239
- throw new Error(`Wrong object type for transaction options: ${opts}`);
244
+ if (opts !== undefined)
245
+ validateObject(opts, {}, {}, 'opts');
240
246
  const _opts = {
241
247
  ...opts,
242
248
  // Defaults
@@ -274,10 +280,13 @@ function validateOpts(opts) {
274
280
  throw new Error(`Transation options wrong type: ${k}=${v} (${typeof v})`);
275
281
  }
276
282
  // 0 and -1 happens in tests
283
+ // With allowUnknownVersion any numeric version is fine; the ternary was inverted
284
+ // before 2026-07 (audit), which made the option throw for every numeric version.
277
285
  if (_opts.allowUnknownVersion
278
- ? typeof _opts.version === 'number'
286
+ ? typeof _opts.version !== 'number'
279
287
  : ![-1, 0, 1, 2, 3].includes(_opts.version))
280
288
  throw new Error(`Unknown version: ${_opts.version}`);
289
+ P.I32LE.encode(_opts.version); // Validate the signed transaction-version wire domain.
281
290
  if (_opts.customScripts !== undefined) {
282
291
  const cs = _opts.customScripts;
283
292
  if (!Array.isArray(cs)) {
@@ -294,6 +303,7 @@ function validateOpts(opts) {
294
303
  }
295
304
  // NOTE: we cannot do this inside PSBTInput coder, because there is no index/txid at this point!
296
305
  function validateInput(i) {
306
+ validateObject(i, {}, {}, 'i');
297
307
  const _i = i;
298
308
  if (_i.nonWitnessUtxo && _i.index !== undefined) {
299
309
  const last = _i.nonWitnessUtxo.outputs.length - 1;
@@ -320,6 +330,9 @@ function validateInput(i) {
320
330
  allowUnknownOutputs: true,
321
331
  disableScriptCheck: true,
322
332
  allowUnknownInputs: true,
333
+ // Consensus does not restrict nVersion; a previous tx with a non-standard
334
+ // version is still spendable and its txid must still be verifiable.
335
+ allowUnknownVersion: true,
323
336
  });
324
337
  const txid = hex.encode(_i.txid);
325
338
  // BIP174 requires the provided nonWitnessUtxo to hash to the prevout txid even when the
@@ -346,6 +359,7 @@ function validateInput(i) {
346
359
  * ```
347
360
  */
348
361
  export function getPrevOut(input) {
362
+ validateObject(input, {}, {}, 'input');
349
363
  const _input = input;
350
364
  if (_input.nonWitnessUtxo) {
351
365
  if (_input.index === undefined)
@@ -359,8 +373,15 @@ export function getPrevOut(input) {
359
373
  throw new Error(`Wrong input index=${_input.index}`);
360
374
  return _input.nonWitnessUtxo.outputs[_input.index];
361
375
  }
362
- else if (_input.witnessUtxo)
363
- return _input.witnessUtxo;
376
+ else if ('witnessUtxo' in _input) {
377
+ // The presence check catches malformed provided values; narrow after the guard for TS.
378
+ const prev = _input.witnessUtxo;
379
+ validateObject(prev, {}, {}, 'input.witnessUtxo');
380
+ abigint(prev.amount, 'input.witnessUtxo.amount');
381
+ if (!isBytes(prev.script))
382
+ throw new TypeError('"input.witnessUtxo.script" expected Uint8Array, got type=' + typeof prev.script);
383
+ return prev;
384
+ }
364
385
  else
365
386
  throw new Error('Cannot find previous output info');
366
387
  }
@@ -386,6 +407,11 @@ export function getPrevOut(input) {
386
407
  * ```
387
408
  */
388
409
  export function normalizeInput(i, cur, allowedFields, disableScriptCheck = false, allowUnknown = false) {
410
+ validateObject(i, {}, {}, 'i');
411
+ if (cur !== undefined)
412
+ validateObject(cur, {}, {}, 'cur');
413
+ if (allowedFields !== undefined)
414
+ u.aarray(allowedFields, 'allowedFields');
389
415
  const _i = i;
390
416
  const _cur = cur;
391
417
  const _allowedFields = allowedFields;
@@ -602,6 +628,8 @@ export class Transaction {
602
628
  // Prefer `global.version` when present so cross-version combiners can serialize at the highest
603
629
  // required PSBT version without mutating the frozen transaction options object.
604
630
  toPSBT(PSBTVersion = this.global.version || this.opts.PSBTVersion) {
631
+ if (PSBTVersion !== undefined)
632
+ anumber(PSBTVersion, 'PSBTVersion');
605
633
  if (PSBTVersion !== 0 && PSBTVersion !== 2)
606
634
  throw new Error(`Wrong PSBT version=${PSBTVersion}`);
607
635
  // if (PSBTVersion === 0 && this.inputs.length === 0) {
@@ -774,32 +802,38 @@ export class Transaction {
774
802
  }
775
803
  // Info utils
776
804
  get hasWitnesses() {
777
- let out = false;
778
805
  for (const i of this.inputs)
779
806
  if (i.finalScriptWitness && i.finalScriptWitness.length)
780
- out = true;
781
- return out;
807
+ return true;
808
+ return false;
782
809
  }
783
810
  // https://en.bitcoin.it/wiki/Weight_units
784
811
  get weight() {
785
812
  if (!this.isFinal)
786
813
  throw new Error('Transaction is not finalized');
814
+ // Serialized length of VarBytes(data) without allocating the encoded copy
815
+ const varLen = (dataLen) => CompactSizeLen.encode(dataLen).length + dataLen;
816
+ const hasWitnesses = this.hasWitnesses;
787
817
  let out = 32;
788
818
  // Outputs
789
819
  const outputs = this.outputs.map(outputBeforeSign);
790
820
  out += 4 * CompactSizeLen.encode(this.outputs.length).length;
791
821
  for (const o of outputs)
792
- out += 32 + 4 * VarBytes.encode(o.script).length;
822
+ out += 32 + 4 * varLen(o.script.length);
793
823
  // Inputs
794
- if (this.hasWitnesses)
824
+ if (hasWitnesses)
795
825
  out += 2;
796
826
  out += 4 * CompactSizeLen.encode(this.inputs.length).length;
797
827
  for (const i of this.inputs) {
798
- out += 160 + 4 * VarBytes.encode(i.finalScriptSig || P.EMPTY).length;
828
+ out += 160 + 4 * varLen((i.finalScriptSig || P.EMPTY).length);
799
829
  // Once segwit serialization is active, every input contributes one witness vector, including
800
830
  // legacy inputs whose empty vector still encodes as a single zero-item-count byte.
801
- if (this.hasWitnesses)
802
- out += RawWitness.encode(i.finalScriptWitness || []).length;
831
+ if (hasWitnesses) {
832
+ const witness = i.finalScriptWitness || [];
833
+ out += CompactSizeLen.encode(witness.length).length;
834
+ for (const w of witness)
835
+ out += varLen(w.length);
836
+ }
803
837
  }
804
838
  return out;
805
839
  }
@@ -833,7 +867,8 @@ export class Transaction {
833
867
  }
834
868
  // Input stuff
835
869
  checkInputIdx(idx) {
836
- if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.inputs.length)
870
+ anumber(idx, 'idx');
871
+ if (idx >= this.inputs.length)
837
872
  throw new Error(`Wrong input index=${idx}`);
838
873
  }
839
874
  getInput(idx) {
@@ -845,6 +880,7 @@ export class Transaction {
845
880
  }
846
881
  // Modification
847
882
  addInput(input, _ignoreSignStatus = false) {
883
+ validateObject(input, {}, {}, 'input');
848
884
  if (!_ignoreSignStatus && !this.signStatus().addInput)
849
885
  throw new Error('Tx has signed inputs, cannot add new one');
850
886
  // normalizeInput preserves nested caller-owned byte arrays, so detach them here before the
@@ -866,7 +902,8 @@ export class Transaction {
866
902
  }
867
903
  // Output stuff
868
904
  checkOutputIdx(idx) {
869
- if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.outputs.length)
905
+ anumber(idx, 'idx');
906
+ if (idx >= this.outputs.length)
870
907
  throw new Error(`Wrong output index=${idx}`);
871
908
  }
872
909
  getOutput(idx) {
@@ -883,11 +920,11 @@ export class Transaction {
883
920
  return this.outputs.length;
884
921
  }
885
922
  normalizeOutput(o, cur, allowedFields) {
923
+ validateObject(o, {}, {}, 'o');
886
924
  let { amount, script } = o;
887
925
  if (amount === undefined)
888
926
  amount = cur?.amount;
889
- if (typeof amount !== 'bigint')
890
- throw new Error(`Wrong amount type, should be of type bigint in sats, but got ${amount} of type ${typeof amount}`);
927
+ amount = abigint(amount, 'o.amount');
891
928
  if (typeof script === 'string')
892
929
  script = hex.decode(script);
893
930
  if (script === undefined)
@@ -936,7 +973,7 @@ export class Transaction {
936
973
  }
937
974
  // Utils
938
975
  get fee() {
939
- let res = 0n;
976
+ let res = _0n;
940
977
  for (const i of this.inputs) {
941
978
  const prevOut = getPrevOut(i);
942
979
  if (!prevOut)
@@ -957,7 +994,7 @@ export class Transaction {
957
994
  if (idx < 0 || !Number.isSafeInteger(idx))
958
995
  throw new Error(`Invalid input idx=${idx}`);
959
996
  if ((isSingle && idx >= this.outputs.length) || idx >= this.inputs.length)
960
- return P.U256BE.encode(1n);
997
+ return P.U256BE.encode(_1n);
961
998
  prevOutScript = stripCodeSeparator(prevOutScript);
962
999
  let inputs = this.inputs
963
1000
  .map(inputBeforeSign)
@@ -994,7 +1031,8 @@ export class Transaction {
994
1031
  preimageWitnessV0(idx, prevOutScript, hashType, amount) {
995
1032
  // BIP143 serializes txTo.vin[nIn].prevout and txTo.vin[nIn].nSequence, so reject an invalid
996
1033
  // nIn explicitly instead of leaking a later undefined-input TypeError from inputs[idx].
997
- if (idx < 0 || !Number.isSafeInteger(idx) || idx >= this.inputs.length)
1034
+ anumber(idx, 'idx');
1035
+ if (idx >= this.inputs.length)
998
1036
  throw new Error(`Invalid input idx=${idx}`);
999
1037
  const { isAny, isNone, isSingle } = unpackSighash(hashType);
1000
1038
  let inputHash = EMPTY32;
@@ -1015,15 +1053,18 @@ export class Transaction {
1015
1053
  return u.sha256x2(P.I32LE.encode(this.version), inputHash, sequenceHash, P.bytes(32, true).encode(input.txid), P.U32LE.encode(input.index), VarBytes.encode(prevOutScript), P.U64LE.encode(amount), P.U32LE.encode(input.sequence), outputHash, P.U32LE.encode(this.lockTime), P.U32LE.encode(hashType));
1016
1054
  }
1017
1055
  preimageWitnessV1(idx, prevOutScript, hashType, amount, codeSeparator = -1, leafScript, leafVer = 0xc0, annex) {
1018
- if (!Array.isArray(amount) || this.inputs.length !== amount.length)
1019
- throw new Error(`Invalid amounts array=${amount}`);
1020
- if (!Array.isArray(prevOutScript) || this.inputs.length !== prevOutScript.length)
1021
- throw new Error(`Invalid prevOutScript array=${prevOutScript}`);
1022
1056
  // BIP341 SigMsg commits either to input_index or to the selected input's outpoint/amount/script/
1023
1057
  // sequence under ANYONECANPAY, so reject an invalid index explicitly instead of hashing a
1024
1058
  // nonexistent input or leaking a later integer-encoding RangeError for negative idx.
1025
- if (idx < 0 || !Number.isSafeInteger(idx) || idx >= this.inputs.length)
1059
+ anumber(idx, 'idx');
1060
+ if (idx >= this.inputs.length)
1026
1061
  throw new Error(`Invalid input idx=${idx}`);
1062
+ u.aarray(amount, 'amount');
1063
+ u.aarray(prevOutScript, 'prevOutScript');
1064
+ if (this.inputs.length !== amount.length)
1065
+ throw new Error(`Invalid amounts array=${amount}`);
1066
+ if (this.inputs.length !== prevOutScript.length)
1067
+ throw new Error(`Invalid prevOutScript array=${prevOutScript}`);
1027
1068
  const out = [
1028
1069
  P.U8.encode(0),
1029
1070
  P.U8.encode(hashType), // U8 sigHash
@@ -1063,6 +1104,14 @@ export class Transaction {
1063
1104
  }
1064
1105
  // Signer can be privateKey OR instance of bip32 HD stuff
1065
1106
  signIdx(privateKey, idx, allowedSighash, _auxRand) {
1107
+ if (!isBytes(privateKey)) {
1108
+ // HDKey is a structural external instance, so plain-object validation would
1109
+ // reject valid signers.
1110
+ if (!privateKey ||
1111
+ typeof privateKey !== 'object' ||
1112
+ typeof privateKey.deriveChild !== 'function')
1113
+ throw new TypeError('"privateKey" expected Uint8Array or HDKey, got type=' + typeof privateKey);
1114
+ }
1066
1115
  this.checkInputIdx(idx);
1067
1116
  const input = this.inputs[idx];
1068
1117
  const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
@@ -1260,7 +1309,7 @@ export class Transaction {
1260
1309
  }
1261
1310
  finalizeIdx(idx) {
1262
1311
  this.checkInputIdx(idx);
1263
- if (this.fee < 0n)
1312
+ if (this.fee < _0n)
1264
1313
  throw new Error('Outputs spends more than inputs amount');
1265
1314
  const input = this.inputs[idx];
1266
1315
  const inputType = getInputType(input, this.opts.allowLegacyWitnessUtxo);
@@ -1440,11 +1489,13 @@ export class Transaction {
1440
1489
  throw new Error('Transaction has unfinalized inputs');
1441
1490
  if (!this.outputs.length)
1442
1491
  throw new Error('Transaction has no outputs');
1443
- if (this.fee < 0n)
1492
+ if (this.fee < _0n)
1444
1493
  throw new Error('Outputs spends more than inputs amount');
1445
1494
  return this.toBytes(true, true);
1446
1495
  }
1447
1496
  combine(other) {
1497
+ if (!(other instanceof Transaction))
1498
+ throw new TypeError('"other" expected Transaction, got type=' + typeof other);
1448
1499
  // BIP174 combiners merge same-transaction PSBTs across versions and emit the highest required
1449
1500
  // version, so PSBTVersion mismatches are normalized below instead of treated as conflicts.
1450
1501
  const PSBTVersion = Math.max(this.opts.PSBTVersion || 0, other.opts.PSBTVersion || 0);
@@ -1538,4 +1589,3 @@ export function bip32Path(path) {
1538
1589
  }
1539
1590
  return out;
1540
1591
  }
1541
- //# sourceMappingURL=transaction.js.map
package/utils.d.ts CHANGED
@@ -1,10 +1,40 @@
1
1
  import { sha256 as nobleSha256 } from '@noble/hashes/sha2.js';
2
2
  import { type TArg, type TRet } from '@noble/hashes/utils.js';
3
+ export { abytes, validateObject as vld } from '@noble/curves/utils.js';
3
4
  export { type TArg, type TRet } from '@noble/hashes/utils.js';
4
5
  /** Hex-like input accepted by helpers in this module. */
5
6
  export type Hex = string | Uint8Array;
6
7
  /** Byte array alias used across the library. */
7
8
  export type Bytes = Uint8Array;
9
+ /**
10
+ * Validates that a value is a non-negative bigint.
11
+ * @param n - Value to validate.
12
+ * @param title - Label included in thrown errors.
13
+ * @returns The same bigint.
14
+ * @throws On wrong argument types. {@link TypeError}
15
+ * @example
16
+ * Validate a satoshi amount before transaction encoding.
17
+ * ```ts
18
+ * abigint(1n, 'amount');
19
+ * ```
20
+ */
21
+ export declare function abigint(n: unknown, title?: string): bigint;
22
+ export declare function aarray<T>(item: unknown, title: string, inner?: (elm: T, title: string) => void): T[];
23
+ /**
24
+ * Asserts something is a string.
25
+ * @param value - Value to validate.
26
+ * @param title - Label included in thrown errors.
27
+ * @returns The validated string.
28
+ * @throws On wrong argument types. {@link TypeError}
29
+ * @example
30
+ * Validate a label string.
31
+ *
32
+ * ```ts
33
+ * astring('example', 'label');
34
+ * ```
35
+ */
36
+ export declare function astring(value: unknown, title?: string): string;
37
+ export declare function validateObject(object: Record<string, any>, fields?: Record<string, string>, optFields?: Record<string, string>, _title?: string): void;
8
38
  /**
9
39
  * Checks whether a curve y-coordinate is even.
10
40
  * @param y - y-coordinate to inspect
@@ -280,4 +310,3 @@ export declare function reverseObject<T extends Record<string, string | number>>
280
310
  };
281
311
  /** Union of all value types in an object type. */
282
312
  export type ValueOf<T> = T[keyof T];
283
- //# sourceMappingURL=utils.d.ts.map
package/utils.js CHANGED
@@ -4,10 +4,64 @@ import { ripemd160 } from '@noble/hashes/legacy.js';
4
4
  import { sha256 as nobleSha256 } from '@noble/hashes/sha2.js';
5
5
  import {} from '@noble/hashes/utils.js';
6
6
  import { utils as packedUtils, U32LE } from 'micro-packed';
7
+ export { abytes, validateObject as vld } from '@noble/curves/utils.js';
7
8
  export {} from '@noble/hashes/utils.js';
9
+ /**
10
+ * Validates that a value is a non-negative bigint.
11
+ * @param n - Value to validate.
12
+ * @param title - Label included in thrown errors.
13
+ * @returns The same bigint.
14
+ * @throws On wrong argument types. {@link TypeError}
15
+ * @example
16
+ * Validate a satoshi amount before transaction encoding.
17
+ * ```ts
18
+ * abigint(1n, 'amount');
19
+ * ```
20
+ */
21
+ export function abigint(n, title = 'value') {
22
+ if (typeof n !== 'bigint')
23
+ throw new TypeError(`"${title}" expected bigint, got type=${typeof n}`);
24
+ if (n < _0n)
25
+ throw new RangeError(`"${title}" expected non-negative bigint, got ${n}`);
26
+ return n;
27
+ }
28
+ import { validateObject as vld } from '@noble/curves/utils.js';
29
+ export function aarray(item, title, inner = () => { }) {
30
+ if (!Array.isArray(item))
31
+ throw new TypeError(`"${title}" expected array, got type=${typeof item}`);
32
+ for (let i = 0; i < item.length; i++)
33
+ inner(item[i], `${title}[${i}]`);
34
+ return item;
35
+ }
36
+ /**
37
+ * Asserts something is a string.
38
+ * @param value - Value to validate.
39
+ * @param title - Label included in thrown errors.
40
+ * @returns The validated string.
41
+ * @throws On wrong argument types. {@link TypeError}
42
+ * @example
43
+ * Validate a label string.
44
+ *
45
+ * ```ts
46
+ * astring('example', 'label');
47
+ * ```
48
+ */
49
+ export function astring(value, title = '') {
50
+ if (typeof value !== 'string') {
51
+ const prefix = title && `"${title}" `;
52
+ throw new TypeError(prefix + 'expected string, got type=' + typeof value);
53
+ }
54
+ return value;
55
+ }
56
+ export function validateObject(object, fields = {}, optFields = {}, _title = 'object') {
57
+ return vld(object, fields, optFields);
58
+ }
8
59
  const Point = /* @__PURE__ */ (() => secp.Point)();
9
60
  const Fn = /* @__PURE__ */ (() => Point.Fn)();
10
61
  const CURVE_ORDER = /* @__PURE__ */ (() => Point.Fn.ORDER)();
62
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
63
+ // prettier-ignore
64
+ const _0n = /* @__PURE__ */ BigInt(0), _2n = /* @__PURE__ */ BigInt(2);
11
65
  /**
12
66
  * Checks whether a curve y-coordinate is even.
13
67
  * @param y - y-coordinate to inspect
@@ -18,7 +72,7 @@ const CURVE_ORDER = /* @__PURE__ */ (() => Point.Fn.ORDER)();
18
72
  * hasEven(2n);
19
73
  * ```
20
74
  */
21
- export const hasEven = (y) => y % 2n === 0n;
75
+ export const hasEven = (y) => y % _2n === _0n;
22
76
  /**
23
77
  * Checks whether a value is a Uint8Array.
24
78
  * @param a - value to inspect
@@ -127,7 +181,10 @@ export const pubECDSA = (privateKey, isCompressed) => secp.getPublicKey(privateK
127
181
  // noble/secp256k1 does not support the feature: it is not used outside of BTC.
128
182
  // We implement it manually, because in BTC it's common.
129
183
  // Not best way, but closest to bitcoin implementation (easier to check)
130
- const hasLowR = (sig) => sig.r < CURVE_ORDER / 2n;
184
+ // Hoisted: the bound is constant; no need to redo the bigint division on every
185
+ // grinding-loop iteration. n/2 < 2^255, so r < n/2 guarantees the 32-byte DER r.
186
+ const LOW_R_BOUND = /* @__PURE__ */ (() => CURVE_ORDER / _2n)();
187
+ const hasLowR = (sig) => sig.r < LOW_R_BOUND;
131
188
  /**
132
189
  * Signs a 32-byte hash with ECDSA and returns DER encoding.
133
190
  * @param hash - message hash to sign
@@ -378,4 +435,3 @@ export function reverseObject(obj) {
378
435
  }
379
436
  return res;
380
437
  }
381
- //# sourceMappingURL=utils.js.map
package/utxo.d.ts CHANGED
@@ -558,4 +558,3 @@ export declare function selectUTXO(inputs: TArg<psbt.TransactionInputUpdate[]>,
558
558
  tx: Transaction | undefined;
559
559
  }) | undefined;
560
560
  export {};
561
- //# sourceMappingURL=utxo.d.ts.map
package/utxo.js CHANGED
@@ -2,10 +2,15 @@ import { hex } from '@scure/base';
2
2
  import * as P from 'micro-packed';
3
3
  import { Address, OutScript, checkScript, tapLeafHash } from "./payment.js";
4
4
  import * as psbt from "./psbt.js";
5
- import { CompactSizeLen, RawWitness, Script, VarBytes } from "./script.js";
5
+ import { CompactSizeLen, Script } from "./script.js";
6
6
  import { SignatureHash, Transaction, getInputType, getPrevOut, inputBeforeSign, normalizeInput, toVsize, } from "./transaction.js";
7
- import { NETWORK, PubT, TAPROOT_UNSPENDABLE_KEY, compareBytes, equalBytes, isBytes, sha256, validatePubkey, } from "./utils.js";
7
+ import { abigint, aarray, astring, NETWORK, PubT, TAPROOT_UNSPENDABLE_KEY, compareBytes, equalBytes, isBytes, sha256, validatePubkey, validateObject, } from "./utils.js";
8
8
  const encodeTapBlock = (item) => psbt.TaprootControlBlock.encode(item);
9
+ // Be friendly to bad ECMAScript parsers by not using bigint literals.
10
+ // prettier-ignore
11
+ const _0n = /* @__PURE__ */ BigInt(0), _3n = /* @__PURE__ */ BigInt(3);
12
+ // Serialized length of VarBytes(data) without allocating the encoded copy
13
+ const varLen = (dataLen) => CompactSizeLen.encode(dataLen).length + dataLen;
9
14
  function iterLeafs(tapLeafScript, sigSize, customScripts) {
10
15
  const _tapLeafScript = tapLeafScript;
11
16
  const _customScripts = customScripts;
@@ -150,10 +155,12 @@ function estimateInput(inputType, input, opts) {
150
155
  else if (inputType.txType !== 'segwit')
151
156
  script = inputScript;
152
157
  }
153
- let weight = 160 + 4 * VarBytes.encode(script).length;
158
+ let weight = 160 + 4 * varLen(script.length);
154
159
  let hasWitnesses = false;
155
160
  if (witness) {
156
- weight += RawWitness.encode(witness).length;
161
+ weight += CompactSizeLen.encode(witness.length).length;
162
+ for (const w of witness)
163
+ weight += varLen(w.length);
157
164
  hasWitnesses = true;
158
165
  }
159
166
  return { weight, hasWitnesses };
@@ -163,13 +170,14 @@ export const _cmpBig = (a, b) => {
163
170
  // Array.sort comparators must return a number, so normalize bigint comparisons to -1/0/1
164
171
  // instead of coercing large differences through Number(...) and losing ordering precision.
165
172
  const n = a - b;
166
- if (n < 0n)
173
+ if (n < _0n)
167
174
  return -1;
168
- else if (n > 0n)
175
+ else if (n > _0n)
169
176
  return 1;
170
177
  return 0;
171
178
  };
172
179
  function getScript(o, opts = {}, network = NETWORK) {
180
+ validateObject(o, {}, {}, 'output');
173
181
  const _o = o;
174
182
  const _opts = opts;
175
183
  let script;
@@ -177,20 +185,16 @@ function getScript(o, opts = {}, network = NETWORK) {
177
185
  script = _o.script;
178
186
  }
179
187
  if ('address' in _o) {
180
- if (typeof _o.address !== 'string')
181
- throw new Error(`Estimator: wrong output address=${_o.address}`);
188
+ astring(_o.address, 'output.address');
182
189
  // Address.decode() only yields known descriptors for valid output addresses, but the wrapped
183
190
  // coder type still includes `undefined`, so narrow before re-encoding the script template.
184
191
  script = OutScript.encode(Address(network).decode(_o.address));
185
192
  }
186
193
  if (!script)
187
194
  throw new Error('Estimator: wrong output script');
188
- if (typeof _o.amount !== 'bigint')
189
- throw new Error(`Estimator: wrong output amount=${_o.amount}, should be of type bigint but got ${typeof _o.amount}.`);
190
195
  // Keep selector-only `createTx: false` flows aligned with the transaction/PSBT output boundary:
191
196
  // satoshi-denominated outputs are not allowed to go negative.
192
- if (_o.amount < 0n)
193
- throw new Error(`Estimator: wrong output amount=${_o.amount}`);
197
+ abigint(_o.amount, 'output.amount');
194
198
  if (script && !_opts.allowUnknownOutputs && OutScript.decode(script).type === 'unknown') {
195
199
  throw new Error('Estimator: unknown output script type, there is a chance that input is unspendable. Pass allowUnknownOutputs=true, if you sure');
196
200
  }
@@ -215,12 +219,9 @@ export class _Estimator {
215
219
  constructor(inputs, outputs, opts) {
216
220
  this.outputs = outputs;
217
221
  this.opts = opts;
218
- if (typeof opts.feePerByte !== 'bigint')
219
- throw new Error(`Estimator: wrong feePerByte=${opts.feePerByte}, should be of type bigint but got ${typeof opts.feePerByte}.`);
220
222
  // Zero-fee estimation is useful on regtest/in tests, but negative fee rates would make
221
223
  // `getSatoshi(...)` produce nonsensical negative fees throughout selection.
222
- if (opts.feePerByte < 0n)
223
- throw new Error(`Estimator: feePerByte must be >= 0 satoshi per vbyte`);
224
+ abigint(opts.feePerByte, 'opts.feePerByte');
224
225
  // Dust stuff
225
226
  // TODO: think about this more:
226
227
  // - current dust filters tx which cannot be relayed by core
@@ -233,37 +234,32 @@ export class _Estimator {
233
234
  const inputsDust = 32 + 4 + 1 + 107 + 4; // NOTE: can be smaller for segwit tx?
234
235
  const outputDust = 34; // NOTE: 'nSize = GetSerializeSize(txout)'
235
236
  const dustBytes = opts.dust === undefined ? BigInt(inputsDust + outputDust) : opts.dust;
236
- if (typeof dustBytes !== 'bigint') {
237
- throw new Error(`Estimator: wrong dust=${opts.dust}, should be of type bigint but got ${typeof opts.dust}.`);
238
- }
237
+ abigint(dustBytes, 'opts.dust');
239
238
  // 3 sat/vb is the default minimum fee rate used to calculate dust thresholds by bitcoin core.
240
239
  // 3000 sat/kvb -> 3 sat/vb.
241
240
  // https://github.com/bitcoin/bitcoin/blob/27a770b34b8f1dbb84760f442edb3e23a0c2420b/src/policy/policy.h#L55
242
- const dustFee = opts.dustRelayFeeRate === undefined ? 3n : opts.dustRelayFeeRate;
243
- if (typeof dustFee !== 'bigint') {
244
- throw new Error(`Estimator: wrong dustRelayFeeRate=${opts.dustRelayFeeRate}, should be of type bigint but got ${typeof opts.dustRelayFeeRate}.`);
245
- }
241
+ const dustFee = opts.dustRelayFeeRate === undefined ? _3n : opts.dustRelayFeeRate;
242
+ abigint(dustFee, 'opts.dustRelayFeeRate');
246
243
  // Dust uses feePerbyte by default, but we allow separate dust fee if needed
247
244
  this.dust = dustBytes * dustFee;
248
245
  if (opts.requiredInputs !== undefined && !Array.isArray(opts.requiredInputs))
249
246
  throw new Error(`Estimator: wrong required inputs=${opts.requiredInputs}`);
250
247
  const network = opts.network || NETWORK;
251
- let amount = 0n;
248
+ let amount = _0n;
252
249
  // Base weight: tx with outputs, no inputs
253
250
  let baseWeight = 32;
254
251
  for (const o of outputs) {
255
252
  const script = getScript(o, opts, opts.network);
256
- baseWeight += 32 + 4 * VarBytes.encode(script).length;
253
+ baseWeight += 32 + 4 * varLen(script.length);
257
254
  amount += o.amount;
258
255
  }
259
- if (typeof opts.changeAddress !== 'string')
260
- throw new Error(`Estimator: wrong change address=${opts.changeAddress}`);
256
+ astring(opts.changeAddress, 'opts.changeAddress');
261
257
  let changeWeight = baseWeight +
262
258
  32 +
263
259
  // Same Address.decode() narrowing as above: the estimator only reaches this path for a
264
260
  // concrete change output address, not an unknown descriptor.
265
261
  4 *
266
- VarBytes.encode(OutScript.encode(Address(network).decode(opts.changeAddress))).length;
262
+ varLen(OutScript.encode(Address(network).decode(opts.changeAddress)).length);
267
263
  baseWeight += 4 * CompactSizeLen.encode(outputs.length).length;
268
264
  // If there a lot of outputs change can change fee
269
265
  changeWeight += 4 * CompactSizeLen.encode(outputs.length + 1).length;
@@ -353,7 +349,7 @@ export class _Estimator {
353
349
  let weight = this.opts.alwaysChange ? this.changeWeight : this.baseWeight;
354
350
  let hasWitnesses = false;
355
351
  let num = 0;
356
- let inputsAmount = 0n;
352
+ let inputsAmount = _0n;
357
353
  const targetAmount = this.amount;
358
354
  const res = new Set();
359
355
  let fee;
@@ -401,7 +397,7 @@ export class _Estimator {
401
397
  // Negative: cost of using input is more than value provided (negative)
402
398
  // By default 'blackjack' mode in coinselect doesn't use that, which means
403
399
  // it will use negative output if sorted by 'smallest'
404
- if (skipNegative && value <= 0n)
400
+ if (skipNegative && value <= _0n)
405
401
  continue;
406
402
  weight = newWeight;
407
403
  if (estimate.hasWitnesses)
@@ -415,6 +411,11 @@ export class _Estimator {
415
411
  }
416
412
  if (all) {
417
413
  const total = getTotal(weight, num);
414
+ // 'all' accumulates unconditionally, so sufficiency must be checked here; otherwise
415
+ // result() would report a negative fee (or throw its internal negative-change error).
416
+ // Insufficient funds are a selection failure, same as for accumulation strategies.
417
+ if (targetAmount + total.fee > inputsAmount)
418
+ return undefined;
418
419
  return {
419
420
  indices: Array.from(res),
420
421
  fee: total.fee,
@@ -492,7 +493,7 @@ export class _Estimator {
492
493
  if (needChange) {
493
494
  fee = changeFee;
494
495
  // this shouldn't happen!
495
- if (change < 0n)
496
+ if (change < _0n)
496
497
  throw new Error(`Estimator.result: negative change=${change}`);
497
498
  outputs.push({ address: this.opts.changeAddress, amount: change });
498
499
  }
@@ -550,9 +551,12 @@ export class _Estimator {
550
551
  * ```
551
552
  */
552
553
  export function selectUTXO(inputs, outputs, strategy, opts) {
554
+ aarray(inputs, 'inputs');
555
+ aarray(outputs, 'outputs');
556
+ validateObject(opts, {}, {}, 'opts');
557
+ astring(strategy, 'strategy');
553
558
  // Public wrapper defaults to BIP69 ordering and tx construction unless callers override them.
554
559
  const _opts = { createTx: true, bip69: true, ...opts };
555
560
  const est = new _Estimator(inputs, outputs, _opts);
556
561
  return est.result(strategy);
557
562
  }
558
- //# sourceMappingURL=utxo.js.map
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,YAAY,EAKZ,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,QAAQ,EACR,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EACxE,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,EAAE,EACF,KAAK,EACL,UAAU,EACV,MAAM,EACN,SAAS,GACV,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;GAOG;AAEH,eAAO,MAAM,KAAK,EAAE,IAAI,CACtB,QAAQ,CAAC;IACP,OAAO,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,CAAC,IAAI,UAAU,CAAC;IACzC,WAAW,EAAE,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;IACjE,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC;IACzD,qBAAqB,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;CAC/C,CAAC,CASG,CAAC;AAER,OAAO,EACL,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,EACT,cAAc,EACd,iBAAiB,EACjB,GAAG,GACJ,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
package/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,WAAW,EACX,OAAO,EACP,UAAU,EACV,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,kDAAkD;AAClD,kBAAkB;AAClB,OAAO,EACL,QAAQ,EACR,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EACxE,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,EAAE,EACF,KAAK,EACL,UAAU,EACV,MAAM,EACN,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC;;;;;;;GAOG;AACH,sFAAsF;AACtF,MAAM,CAAC,MAAM,KAAK,GASd,eAAe,CAAC,CAAC,GAAG,EAAE,CACxB,MAAM,CAAC,MAAM,CAAC;IACZ,OAAO;IACP,WAAW;IACX,YAAY;IACZ,UAAU;IACV,qBAAqB;IACrB,kBAAkB;CACnB,CAAC,CAAC,EAAE,CAAC;AAER,OAAO,EACL,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,EACT,cAAc,EACd,iBAAiB,EACjB,GAAG,GACJ,MAAM,cAAc,CAAC;AAGtB,SAAS;AACT,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5D,SAAS;AACT,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
package/musig2.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"musig2.d.ts","sourceRoot":"","sources":["src/musig2.ts"],"names":[],"mappings":"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"}