@scure/btc-signer 1.4.0 → 1.6.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.
Files changed (68) hide show
  1. package/README.md +5 -2
  2. package/_type_test.js +2 -2
  3. package/_type_test.js.map +1 -1
  4. package/esm/_type_test.js +2 -2
  5. package/esm/_type_test.js.map +1 -1
  6. package/esm/index.d.ts +8 -6
  7. package/esm/index.d.ts.map +1 -1
  8. package/esm/index.js +7 -10
  9. package/esm/index.js.map +1 -1
  10. package/esm/payment.d.ts +43 -76
  11. package/esm/payment.d.ts.map +1 -1
  12. package/esm/payment.js +15 -2
  13. package/esm/payment.js.map +1 -1
  14. package/esm/psbt.d.ts +1 -1
  15. package/esm/psbt.d.ts.map +1 -1
  16. package/esm/psbt.js +2 -3
  17. package/esm/psbt.js.map +1 -1
  18. package/esm/script.d.ts +3 -1
  19. package/esm/script.d.ts.map +1 -1
  20. package/esm/script.js.map +1 -1
  21. package/esm/transaction.d.ts +10 -121
  22. package/esm/transaction.d.ts.map +1 -1
  23. package/esm/transaction.js +8 -8
  24. package/esm/transaction.js.map +1 -1
  25. package/esm/utils.d.ts +17 -12
  26. package/esm/utils.d.ts.map +1 -1
  27. package/esm/utils.js +6 -4
  28. package/esm/utils.js.map +1 -1
  29. package/esm/utxo.d.ts +23 -21
  30. package/esm/utxo.d.ts.map +1 -1
  31. package/esm/utxo.js +6 -6
  32. package/esm/utxo.js.map +1 -1
  33. package/index.d.ts +8 -6
  34. package/index.d.ts.map +1 -1
  35. package/index.js +20 -22
  36. package/index.js.map +1 -1
  37. package/package.json +16 -12
  38. package/payment.d.ts +43 -76
  39. package/payment.d.ts.map +1 -1
  40. package/payment.js +14 -1
  41. package/payment.js.map +1 -1
  42. package/psbt.d.ts +1 -1
  43. package/psbt.d.ts.map +1 -1
  44. package/psbt.js +5 -6
  45. package/psbt.js.map +1 -1
  46. package/script.d.ts +3 -1
  47. package/script.d.ts.map +1 -1
  48. package/script.js.map +1 -1
  49. package/src/_type_test.ts +2 -2
  50. package/src/index.ts +25 -14
  51. package/src/payment.ts +74 -27
  52. package/src/psbt.ts +27 -11
  53. package/src/script.ts +5 -5
  54. package/src/transaction.ts +50 -36
  55. package/src/utils.ts +26 -16
  56. package/src/utxo.ts +30 -18
  57. package/transaction.d.ts +10 -121
  58. package/transaction.d.ts.map +1 -1
  59. package/transaction.js +17 -17
  60. package/transaction.js.map +1 -1
  61. package/utils.d.ts +17 -12
  62. package/utils.d.ts.map +1 -1
  63. package/utils.js +6 -4
  64. package/utils.js.map +1 -1
  65. package/utxo.d.ts +23 -21
  66. package/utxo.d.ts.map +1 -1
  67. package/utxo.js +4 -4
  68. package/utxo.js.map +1 -1
@@ -1,12 +1,19 @@
1
- import * as P from 'micro-packed';
2
1
  import { hex } from '@scure/base';
3
- import { Address, CustomScript, OutScript, checkScript, tapLeafHash } from './payment.js';
2
+ import * as P from 'micro-packed';
3
+ import { Address, type CustomScript, OutScript, checkScript, tapLeafHash } from './payment.js';
4
4
  import * as psbt from './psbt.js'; // circular
5
- import { CompactSizeLen, Script, VarBytes } from './script.js';
6
- import { RawOutput, RawTx, RawOldTx, RawWitness } from './script.js';
7
- import { NETWORK, Bytes, concatBytes, isBytes, equalBytes } from './utils.js';
5
+ import {
6
+ CompactSizeLen,
7
+ RawOldTx,
8
+ RawOutput,
9
+ RawTx,
10
+ RawWitness,
11
+ Script,
12
+ VarBytes,
13
+ } from './script.js';
8
14
  import * as u from './utils.js';
9
- import { getInputType, toVsize, normalizeInput, getPrevOut } from './utxo.js'; // circular
15
+ import { type Bytes, NETWORK, concatBytes, equalBytes, isBytes } from './utils.js';
16
+ import { getInputType, getPrevOut, normalizeInput, toVsize } from './utxo.js'; // circular
10
17
 
11
18
  const EMPTY32 = new Uint8Array(32);
12
19
  const EMPTY_OUTPUT: P.UnwrapCoder<typeof RawOutput> = {
@@ -30,15 +37,15 @@ export const PRECISION = 8;
30
37
  export const DEFAULT_VERSION = 2;
31
38
  export const DEFAULT_LOCKTIME = 0;
32
39
  export const DEFAULT_SEQUENCE = 4294967295;
33
- export const Decimal = P.coders.decimal(PRECISION);
40
+ export const Decimal: P.Coder<bigint, string> = P.coders.decimal(PRECISION);
34
41
 
35
42
  // Same as value || def, but doesn't overwrites zero ('0', 0, 0n, etc)
36
- export const def = <T>(value: T | undefined, def: T) => (value === undefined ? def : value);
43
+ export const def = <T>(value: T | undefined, def: T): T => (value === undefined ? def : value);
37
44
 
38
45
  export function cloneDeep<T>(obj: T): T {
39
46
  if (Array.isArray(obj)) return obj.map((i) => cloneDeep(i)) as unknown as T;
40
47
  // slice of nodejs Buffer doesn't copy
41
- else if (obj instanceof Uint8Array) return Uint8Array.from(obj) as unknown as T;
48
+ else if (isBytes(obj)) return Uint8Array.from(obj) as unknown as T;
42
49
  // immutable
43
50
  else if (['number', 'bigint', 'boolean', 'string', 'undefined'].includes(typeof obj)) return obj;
44
51
  // null is object
@@ -166,7 +173,7 @@ function unpackSighash(hashType: number) {
166
173
  };
167
174
  }
168
175
 
169
- function validateOpts(opts: TxOpts) {
176
+ function validateOpts(opts: TxOpts): Readonly<TxOpts> {
170
177
  if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')
171
178
  throw new Error(`Wrong object type for transaction options: ${opts}`);
172
179
 
@@ -182,7 +189,8 @@ function validateOpts(opts: TxOpts) {
182
189
  if (typeof _opts.allowUnknowOutput !== 'undefined')
183
190
  opts.allowUnknownOutputs = _opts.allowUnknowOutput;
184
191
  // 0 and -1 happens in tests
185
- if (![-1, 0, 1, 2].includes(_opts.version)) throw new Error(`Unknown version: ${_opts.version}`);
192
+ if (![-1, 0, 1, 2, 3].includes(_opts.version))
193
+ throw new Error(`Unknown version: ${_opts.version}`);
186
194
  if (typeof _opts.lockTime !== 'number') throw new Error('Transaction lock time should be number');
187
195
  P.U32LE.encode(_opts.lockTime); // Additional range checks that lockTime
188
196
  // There is no PSBT v1, and any new version will probably have fields which we don't know how to parse, which
@@ -233,7 +241,7 @@ export class Transaction {
233
241
  }
234
242
 
235
243
  // Import
236
- static fromRaw(raw: Bytes, opts: TxOpts = {}) {
244
+ static fromRaw(raw: Bytes, opts: TxOpts = {}): Transaction {
237
245
  const parsed = RawTx.decode(raw);
238
246
  const tx = new Transaction({ ...opts, version: parsed.version, lockTime: parsed.lockTime });
239
247
  for (const o of parsed.outputs) tx.addOutput(o);
@@ -246,7 +254,7 @@ export class Transaction {
246
254
  return tx;
247
255
  }
248
256
  // PSBT
249
- static fromPSBT(psbt_: Bytes, opts: TxOpts = {}) {
257
+ static fromPSBT(psbt_: Bytes, opts: TxOpts = {}): Transaction {
250
258
  let parsed: P.UnwrapCoder<typeof psbt.RawPSBTV0>;
251
259
  try {
252
260
  parsed = psbt.RawPSBTV0.decode(psbt_);
@@ -281,7 +289,7 @@ export class Transaction {
281
289
  if (lockTime !== DEFAULT_LOCKTIME) tx.global.fallbackLocktime = lockTime;
282
290
  return tx;
283
291
  }
284
- toPSBT(PSBTVersion = this.opts.PSBTVersion) {
292
+ toPSBT(PSBTVersion: number | undefined = this.opts.PSBTVersion): Uint8Array {
285
293
  if (PSBTVersion !== 0 && PSBTVersion !== 2)
286
294
  throw new Error(`Wrong PSBT version=${PSBTVersion}`);
287
295
  // if (PSBTVersion === 0 && this.inputs.length === 0) {
@@ -338,7 +346,7 @@ export class Transaction {
338
346
  }
339
347
 
340
348
  // BIP370 lockTime (https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#determining-lock-time)
341
- get lockTime() {
349
+ get lockTime(): number {
342
350
  let height = DEFAULT_LOCKTIME;
343
351
  let heightCnt = 0;
344
352
  let time = DEFAULT_LOCKTIME;
@@ -358,7 +366,7 @@ export class Transaction {
358
366
  return this.global.fallbackLocktime || DEFAULT_LOCKTIME;
359
367
  }
360
368
 
361
- get version() {
369
+ get version(): number {
362
370
  // Should be not possible
363
371
  if (this.global.txVersion === undefined) throw new Error('No global.txVersion');
364
372
  return this.global.txVersion;
@@ -381,7 +389,8 @@ export class Transaction {
381
389
  // We will lose some vectors -> smaller test coverage of preimages (very important!)
382
390
  private inputSighash(idx: number) {
383
391
  this.checkInputIdx(idx);
384
- const sighash = getInputType(this.inputs[idx], this.opts.allowLegacyWitnessUtxo).sighash;
392
+ const inputSighash = this.inputs[idx].sighashType;
393
+ const sighash = inputSighash === undefined ? SignatureHash.DEFAULT : inputSighash;
385
394
  // ALL or DEFAULT -- everything signed
386
395
  // NONE -- all inputs + no outputs
387
396
  // SINGLE -- all inputs + output with same index
@@ -418,7 +427,7 @@ export class Transaction {
418
427
  return { addInput, addOutput, inputs, outputs };
419
428
  }
420
429
 
421
- get isFinal() {
430
+ get isFinal(): boolean {
422
431
  for (let idx = 0; idx < this.inputs.length; idx++)
423
432
  if (this.inputStatus(idx) !== 'finalized') return false;
424
433
  return true;
@@ -452,7 +461,7 @@ export class Transaction {
452
461
  get vsize(): number {
453
462
  return toVsize(this.weight);
454
463
  }
455
- toBytes(withScriptSig = false, withWitness = false) {
464
+ toBytes(withScriptSig = false, withWitness = false): Uint8Array {
456
465
  return RawTx.encode({
457
466
  version: this.version,
458
467
  lockTime: this.lockTime,
@@ -468,15 +477,15 @@ export class Transaction {
468
477
  get unsignedTx(): Bytes {
469
478
  return this.toBytes(false, false);
470
479
  }
471
- get hex() {
480
+ get hex(): string {
472
481
  return hex.encode(this.toBytes(true, this.hasWitnesses));
473
482
  }
474
483
 
475
- get hash() {
484
+ get hash(): string {
476
485
  if (!this.isFinal) throw new Error('Transaction is not finalized');
477
486
  return hex.encode(u.sha256x2(this.toBytes(true)));
478
487
  }
479
- get id() {
488
+ get id(): string {
480
489
  if (!this.isFinal) throw new Error('Transaction is not finalized');
481
490
  return hex.encode(u.sha256x2(this.toBytes(true)).reverse());
482
491
  }
@@ -485,11 +494,11 @@ export class Transaction {
485
494
  if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.inputs.length)
486
495
  throw new Error(`Wrong input index=${idx}`);
487
496
  }
488
- getInput(idx: number) {
497
+ getInput(idx: number): psbt.TransactionInput {
489
498
  this.checkInputIdx(idx);
490
499
  return cloneDeep(this.inputs[idx]);
491
500
  }
492
- get inputsLength() {
501
+ get inputsLength(): number {
493
502
  return this.inputs.length;
494
503
  }
495
504
  // Modification
@@ -499,7 +508,7 @@ export class Transaction {
499
508
  this.inputs.push(normalizeInput(input, undefined, undefined, this.opts.disableScriptCheck));
500
509
  return this.inputs.length - 1;
501
510
  }
502
- updateInput(idx: number, input: psbt.TransactionInputUpdate, _ignoreSignStatus = false) {
511
+ updateInput(idx: number, input: psbt.TransactionInputUpdate, _ignoreSignStatus = false): void {
503
512
  this.checkInputIdx(idx);
504
513
  let allowedFields = undefined;
505
514
  if (!_ignoreSignStatus) {
@@ -519,17 +528,17 @@ export class Transaction {
519
528
  if (!Number.isSafeInteger(idx) || 0 > idx || idx >= this.outputs.length)
520
529
  throw new Error(`Wrong output index=${idx}`);
521
530
  }
522
- getOutput(idx: number) {
531
+ getOutput(idx: number): psbt.TransactionOutput {
523
532
  this.checkOutputIdx(idx);
524
533
  return cloneDeep(this.outputs[idx]);
525
534
  }
526
- getOutputAddress(idx: number, network = NETWORK): string | undefined {
535
+ getOutputAddress(idx: number, network: u.BTC_NETWORK = NETWORK): string | undefined {
527
536
  const out = this.getOutput(idx);
528
537
  if (!out.script) return;
529
538
  return Address(network).encode(OutScript.decode(out.script));
530
539
  }
531
540
 
532
- get outputsLength() {
541
+ get outputsLength(): number {
533
542
  return this.outputs.length;
534
543
  }
535
544
  private normalizeOutput(
@@ -567,7 +576,7 @@ export class Transaction {
567
576
  this.outputs.push(this.normalizeOutput(o));
568
577
  return this.outputs.length - 1;
569
578
  }
570
- updateOutput(idx: number, output: psbt.TransactionOutputUpdate, _ignoreSignStatus = false) {
579
+ updateOutput(idx: number, output: psbt.TransactionOutputUpdate, _ignoreSignStatus = false): void {
571
580
  this.checkOutputIdx(idx);
572
581
  let allowedFields = undefined;
573
582
  if (!_ignoreSignStatus) {
@@ -577,7 +586,7 @@ export class Transaction {
577
586
  }
578
587
  this.outputs[idx] = this.normalizeOutput(output, this.outputs[idx], allowedFields);
579
588
  }
580
- addOutputAddress(address: string, amount: bigint, network = NETWORK): number {
589
+ addOutputAddress(address: string, amount: bigint, network: u.BTC_NETWORK = NETWORK): number {
581
590
  return this.addOutput({ script: OutScript.encode(Address(network).decode(address)), amount });
582
591
  }
583
592
  // Utils
@@ -632,7 +641,12 @@ export class Transaction {
632
641
  });
633
642
  return u.sha256x2(tmpTx, P.I32LE.encode(hashType));
634
643
  }
635
- preimageWitnessV0(idx: number, prevOutScript: Bytes, hashType: number, amount: bigint) {
644
+ preimageWitnessV0(
645
+ idx: number,
646
+ prevOutScript: Bytes,
647
+ hashType: number,
648
+ amount: bigint
649
+ ): Uint8Array {
636
650
  const { isAny, isNone, isSingle } = unpackSighash(hashType);
637
651
  let inputHash = EMPTY32;
638
652
  let sequenceHash = EMPTY32;
@@ -670,7 +684,7 @@ export class Transaction {
670
684
  leafScript?: Bytes,
671
685
  leafVer = 0xc0,
672
686
  annex?: Bytes
673
- ) {
687
+ ): Uint8Array {
674
688
  if (!Array.isArray(amount) || this.inputs.length !== amount.length)
675
689
  throw new Error(`Invalid amounts array=${amount}`);
676
690
  if (!Array.isArray(prevOutScript) || this.inputs.length !== prevOutScript.length)
@@ -877,7 +891,7 @@ export class Transaction {
877
891
  return num;
878
892
  }
879
893
 
880
- finalizeIdx(idx: number) {
894
+ finalizeIdx(idx: number): void {
881
895
  this.checkInputIdx(idx);
882
896
  if (this.fee < 0n) throw new Error('Outputs spends more than inputs amount');
883
897
  const input = this.inputs[idx];
@@ -1026,10 +1040,10 @@ export class Transaction {
1026
1040
  if (finalScriptWitness) input.finalScriptWitness = finalScriptWitness;
1027
1041
  cleanFinalInput(input);
1028
1042
  }
1029
- finalize() {
1043
+ finalize(): void {
1030
1044
  for (let i = 0; i < this.inputs.length; i++) this.finalizeIdx(i);
1031
1045
  }
1032
- extract() {
1046
+ extract(): Uint8Array {
1033
1047
  if (!this.isFinal) throw new Error('Transaction has unfinalized inputs');
1034
1048
  if (!this.outputs.length) throw new Error('Transaction has no outputs');
1035
1049
  if (this.fee < 0n) throw new Error('Outputs spends more than inputs amount');
@@ -1061,7 +1075,7 @@ export class Transaction {
1061
1075
  for (let i = 0; i < this.outputs.length; i++) this.updateOutput(i, other.outputs[i], true);
1062
1076
  return this;
1063
1077
  }
1064
- clone() {
1078
+ clone(): Transaction {
1065
1079
  // deepClone probably faster, but this enforces that encoding is valid
1066
1080
  return Transaction.fromPSBT(this.toPSBT(this.opts.PSBTVersion), this.opts);
1067
1081
  }
package/src/utils.ts CHANGED
@@ -1,20 +1,24 @@
1
- import { utils as packedUtils, U32LE } from 'micro-packed';
1
+ import { schnorr, secp256k1 as secp } from '@noble/curves/secp256k1';
2
2
  import { ripemd160 } from '@noble/hashes/ripemd160';
3
3
  import { sha256 } from '@noble/hashes/sha256';
4
- import { secp256k1 as secp, schnorr } from '@noble/curves/secp256k1';
4
+ import { utils as packedUtils, U32LE } from 'micro-packed';
5
5
 
6
+ export type Hex = string | Uint8Array;
6
7
  export type Bytes = Uint8Array;
7
8
  const Point = secp.ProjectivePoint;
8
9
  const CURVE_ORDER = secp.CURVE.n;
9
10
 
10
- const { isBytes, concatBytes, equalBytes } = packedUtils;
11
- export { sha256, isBytes, concatBytes, equalBytes };
11
+ const isBytes: (a: unknown) => a is Uint8Array = packedUtils.isBytes;
12
+ const concatBytes: (...arrays: Uint8Array[]) => Uint8Array = packedUtils.concatBytes;
13
+ const equalBytes: (a: Uint8Array, b: Uint8Array) => boolean = packedUtils.equalBytes;
14
+ export { concatBytes, equalBytes, isBytes, sha256 };
12
15
 
13
- export const hash160 = (msg: Bytes) => ripemd160(sha256(msg));
14
- export const sha256x2 = (...msgs: Bytes[]) => sha256(sha256(concatBytes(...msgs)));
15
- export const randomPrivateKeyBytes = schnorr.utils.randomPrivateKey;
16
+ export const hash160 = (msg: Uint8Array): Uint8Array => ripemd160(sha256(msg));
17
+ export const sha256x2 = (...msgs: Uint8Array[]): Uint8Array => sha256(sha256(concatBytes(...msgs)));
18
+ export const randomPrivateKeyBytes: () => Uint8Array = schnorr.utils.randomPrivateKey;
16
19
  export const pubSchnorr = schnorr.getPublicKey as (priv: string | Uint8Array) => Uint8Array;
17
- export const pubECDSA = secp.getPublicKey;
20
+ export const pubECDSA: (privateKey: string | Uint8Array, isCompressed?: boolean) => Uint8Array =
21
+ secp.getPublicKey;
18
22
 
19
23
  // low-r signature grinding. Used to reduce tx size by 1 byte.
20
24
  // noble/secp256k1 does not support the feature: it is not used outside of BTC.
@@ -35,8 +39,8 @@ export function signECDSA(hash: Bytes, privateKey: Bytes, lowR = false): Bytes {
35
39
  return sig.toDERRawBytes();
36
40
  }
37
41
 
38
- export const signSchnorr = schnorr.sign;
39
- export const tagSchnorr = schnorr.utils.taggedHash;
42
+ export const signSchnorr: typeof schnorr.sign = schnorr.sign;
43
+ export const tagSchnorr: typeof schnorr.utils.taggedHash = schnorr.utils.taggedHash;
40
44
 
41
45
  export enum PubT {
42
46
  ecdsa,
@@ -65,7 +69,7 @@ export function tapTweak(a: Bytes, b: Bytes): bigint {
65
69
  return tn;
66
70
  }
67
71
 
68
- export function taprootTweakPrivKey(privKey: Uint8Array, merkleRoot = new Uint8Array()) {
72
+ export function taprootTweakPrivKey(privKey: Bytes, merkleRoot: Bytes = new Uint8Array()): Bytes {
69
73
  const u = schnorr.utils;
70
74
  const seckey0 = u.bytesToNumberBE(privKey); // seckey0 = int_from_bytes(seckey0)
71
75
  const P = Point.fromPrivateKey(seckey0); // P = point_mul(G, seckey0)
@@ -78,7 +82,7 @@ export function taprootTweakPrivKey(privKey: Uint8Array, merkleRoot = new Uint8A
78
82
  return u.numberToBytesBE(u.mod(seckey + t, CURVE_ORDER), 32);
79
83
  }
80
84
 
81
- export function taprootTweakPubkey(pubKey: Uint8Array, h: Uint8Array): [Uint8Array, number] {
85
+ export function taprootTweakPubkey(pubKey: Bytes, h: Bytes): [Bytes, number] {
82
86
  const u = schnorr.utils;
83
87
  const t = tapTweak(pubKey, h); // t = int_from_bytes(tagged_hash("TapTweak", pubkey + h))
84
88
  const P = u.lift_x(u.bytesToNumberBE(pubKey)); // P = lift_x(int_from_bytes(pubkey))
@@ -93,16 +97,22 @@ export function taprootTweakPubkey(pubKey: Uint8Array, h: Uint8Array): [Uint8Arr
93
97
  // It is possible to switch SECP256K1_GENERATOR_POINT with some random point;
94
98
  // but it's too complex to prove.
95
99
  // Also used by bitcoin-core and bitcoinjs-lib
96
- export const TAPROOT_UNSPENDABLE_KEY = sha256(Point.BASE.toRawBytes(false));
100
+ export const TAPROOT_UNSPENDABLE_KEY: Bytes = sha256(Point.BASE.toRawBytes(false));
97
101
 
98
- export const NETWORK = {
102
+ export type BTC_NETWORK = {
103
+ bech32: string;
104
+ pubKeyHash: number;
105
+ scriptHash: number;
106
+ wif: number;
107
+ };
108
+ export const NETWORK: BTC_NETWORK = {
99
109
  bech32: 'bc',
100
110
  pubKeyHash: 0x00,
101
111
  scriptHash: 0x05,
102
112
  wif: 0x80,
103
113
  };
104
114
 
105
- export const TEST_NETWORK: typeof NETWORK = {
115
+ export const TEST_NETWORK: BTC_NETWORK = {
106
116
  bech32: 'tb',
107
117
  pubKeyHash: 0x6f,
108
118
  scriptHash: 0xc4,
@@ -110,7 +120,7 @@ export const TEST_NETWORK: typeof NETWORK = {
110
120
  };
111
121
 
112
122
  // Exported for tests, internal method
113
- export function compareBytes(a: Bytes, b: Bytes) {
123
+ export function compareBytes(a: Bytes, b: Bytes): number {
114
124
  if (!isBytes(a) || !isBytes(b)) throw new Error(`cmp: wrong type a=${typeof a} b=${typeof b}`);
115
125
  // -1 -> a<b, 0 -> a==b, 1 -> a>b
116
126
  const len = Math.min(a.length, b.length);
package/src/utxo.ts CHANGED
@@ -1,25 +1,28 @@
1
1
  import { hex } from '@scure/base';
2
2
  import * as P from 'micro-packed';
3
- import { Address, CustomScript, OutScript, checkScript, tapLeafHash } from './payment.js';
3
+ import { Address, type CustomScript, OutScript, checkScript, tapLeafHash } from './payment.js';
4
4
  import * as psbt from './psbt.js';
5
5
  import { CompactSizeLen, RawOutput, RawTx, RawWitness, Script, VarBytes } from './script.js';
6
6
  import {
7
7
  DEFAULT_SEQUENCE,
8
- TxOpts,
9
- inputBeforeSign,
10
8
  SignatureHash,
11
9
  Transaction,
10
+ type TxOpts,
11
+ inputBeforeSign,
12
12
  } from './transaction.js'; // circular
13
13
  import {
14
+ type Bytes,
14
15
  NETWORK,
15
- Bytes,
16
+ PubT,
17
+ TAPROOT_UNSPENDABLE_KEY,
16
18
  compareBytes,
17
19
  equalBytes,
18
20
  isBytes,
19
- TAPROOT_UNSPENDABLE_KEY,
20
21
  sha256,
22
+ validatePubkey,
21
23
  } from './utils.js';
22
- import { validatePubkey, PubT } from './utils.js';
24
+
25
+ export type PSBTInputs = psbt.PSBTKeyMapKeys<typeof psbt.PSBTInput>;
23
26
 
24
27
  // Normalizes input
25
28
  export function getPrevOut(input: psbt.TransactionInput): P.UnwrapCoder<typeof RawOutput> {
@@ -48,7 +51,7 @@ export function normalizeInput(
48
51
  if (typeof txid === 'string') txid = hex.decode(txid);
49
52
  // TODO: if we have nonWitnessUtxo, we can extract txId from here
50
53
  if (txid === undefined) txid = cur?.txid;
51
- let res: psbt.PSBTKeyMapKeys<typeof psbt.PSBTInput> = { ...cur, ...i, nonWitnessUtxo, txid };
54
+ let res: PSBTInputs = { ...cur, ...i, nonWitnessUtxo, txid };
52
55
  if (!('nonWitnessUtxo' in i) && res.nonWitnessUtxo === undefined) delete res.nonWitnessUtxo;
53
56
  if (res.sequence === undefined) res.sequence = DEFAULT_SEQUENCE;
54
57
  if (res.tapMerkleRoot === null) delete res.tapMerkleRoot;
@@ -122,9 +125,17 @@ export function getInputType(input: psbt.TransactionInput, allowLegacyWitnessUtx
122
125
  }
123
126
  }
124
127
 
125
- export const toVsize = (weight: number) => Math.ceil(weight / 4);
128
+ export const toVsize = (weight: number): number => Math.ceil(weight / 4);
126
129
  // UTXO Select
127
- type Output = { address: string; amount: bigint } | { script: Uint8Array; amount: bigint };
130
+ export type Output = { address: string; amount: bigint } | { script: Uint8Array; amount: bigint };
131
+ export type Accumulated =
132
+ | {
133
+ indices: number[];
134
+ fee: bigint | undefined;
135
+ weight: number;
136
+ total: bigint;
137
+ }
138
+ | undefined;
128
139
  type TapLeafScript = psbt.TransactionInput['tapLeafScript'];
129
140
  type TB = Parameters<typeof psbt.TaprootControlBlock.encode>[0];
130
141
  const encodeTapBlock = (item: TB) => psbt.TaprootControlBlock.encode(item);
@@ -252,7 +263,7 @@ function estimateInput(
252
263
  }
253
264
 
254
265
  // Exported for tests, internal method
255
- export const _cmpBig = (a: bigint, b: bigint) => {
266
+ export const _cmpBig = (a: bigint, b: bigint): 0 | 1 | -1 => {
256
267
  const n = a - b;
257
268
  if (n < 0n) return -1;
258
269
  else if (n > 0n) return 1;
@@ -276,7 +287,7 @@ export type EstimatorOpts = TxOpts & {
276
287
 
277
288
  function getScript(o: Output, opts: TxOpts = {}, network = NETWORK) {
278
289
  let script;
279
- if ('script' in o && o.script instanceof Uint8Array) {
290
+ if ('script' in o && isBytes(o.script)) {
280
291
  script = o.script;
281
292
  }
282
293
  if ('address' in o) {
@@ -442,27 +453,27 @@ export class _Estimator {
442
453
  }
443
454
 
444
455
  // Sort by value instead of amount
445
- get biggest() {
456
+ get biggest(): number[] {
446
457
  return this.normalizedInputs
447
458
  .map((_i, j) => j)
448
459
  .sort((a, b) => _cmpBig(this.normalizedInputs[b].value, this.normalizedInputs[a].value));
449
460
  }
450
- get smallest() {
461
+ get smallest(): number[] {
451
462
  return this.biggest.reverse();
452
463
  }
453
464
  // These assume that UTXO array has historical order.
454
465
  // Otherwise, we have no way to know which tx is oldest
455
466
  // Explorers usually give UTXO in this order.
456
- get oldest() {
467
+ get oldest(): number[] {
457
468
  return this.normalizedInputs.map((_i, j) => j);
458
469
  }
459
- get newest() {
470
+ get newest(): number[] {
460
471
  return this.oldest.reverse();
461
472
  }
462
473
  // exact - like blackjack from coinselect.
463
474
  // exact(biggest) will select one big utxo which is closer to targetValue+dust, if possible.
464
475
  // If not, it will accumulate largest utxo until value is close to targetValue+dust.
465
- accumulate(indices: number[], exact = false, skipNegative = true, all = false) {
476
+ accumulate(indices: number[], exact = false, skipNegative = true, all = false): Accumulated {
466
477
  // TODO: how to handle change addresses?
467
478
  // - cost of input
468
479
  // - cost of change output (if input requires change)
@@ -524,7 +535,7 @@ export class _Estimator {
524
535
  }
525
536
 
526
537
  // Works like coinselect default method
527
- default() {
538
+ default(): Accumulated {
528
539
  const { biggest } = this;
529
540
  const exact = this.accumulate(biggest, true, false);
530
541
  if (exact) return exact;
@@ -602,7 +613,8 @@ export class _Estimator {
602
613
  for (const o of outputs)
603
614
  tx.addOutput({ ...o, script: getScript(o, this.opts, this.opts.network) });
604
615
  }
605
- return { ...res, tx };
616
+ return Object.assign(res, { tx });
617
+ // return { ...res, tx: tx };
606
618
  }
607
619
  }
608
620
 
package/transaction.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as P from 'micro-packed';
2
- import { CustomScript } from './payment.js';
2
+ import { type CustomScript } from './payment.js';
3
3
  import * as psbt from './psbt.js';
4
- import { Bytes } from './utils.js';
4
+ import * as u from './utils.js';
5
+ import { type Bytes } from './utils.js';
5
6
  interface HDKey {
6
7
  publicKey: Bytes;
7
8
  privateKey: Bytes;
@@ -15,10 +16,7 @@ export declare const PRECISION = 8;
15
16
  export declare const DEFAULT_VERSION = 2;
16
17
  export declare const DEFAULT_LOCKTIME = 0;
17
18
  export declare const DEFAULT_SEQUENCE = 4294967295;
18
- export declare const Decimal: {
19
- encode: (from: bigint) => string;
20
- decode: (to: string) => bigint;
21
- };
19
+ export declare const Decimal: P.Coder<bigint, string>;
22
20
  export declare const def: <T>(value: T | undefined, def: T) => T;
23
21
  export declare function cloneDeep<T>(obj: T): T;
24
22
  export type TxOpts = {
@@ -65,22 +63,7 @@ export type TransactionInputRequired = {
65
63
  finalScriptSig: Bytes;
66
64
  };
67
65
  export declare function inputBeforeSign(i: psbt.TransactionInput): TransactionInputRequired;
68
- declare function validateOpts(opts: TxOpts): Readonly<{
69
- version: number;
70
- lockTime: number;
71
- PSBTVersion: number;
72
- /** @deprecated Use `allowUnknownOutputs` */
73
- allowUnknowOutput?: boolean;
74
- allowUnknownOutputs?: boolean;
75
- /** @deprecated Use `allowUnknownInputs` */
76
- allowUnknowInput?: boolean;
77
- allowUnknownInputs?: boolean;
78
- disableScriptCheck?: boolean;
79
- bip174jsCompat?: boolean;
80
- allowLegacyWitnessUtxo?: boolean;
81
- lowR?: boolean;
82
- customScripts?: CustomScript[];
83
- }>;
66
+ declare function validateOpts(opts: TxOpts): Readonly<TxOpts>;
84
67
  export declare class Transaction {
85
68
  private global;
86
69
  private inputs;
@@ -89,7 +72,7 @@ export declare class Transaction {
89
72
  constructor(opts?: TxOpts);
90
73
  static fromRaw(raw: Bytes, opts?: TxOpts): Transaction;
91
74
  static fromPSBT(psbt_: Bytes, opts?: TxOpts): Transaction;
92
- toPSBT(PSBTVersion?: number): Uint8Array;
75
+ toPSBT(PSBTVersion?: number | undefined): Uint8Array;
93
76
  get lockTime(): number;
94
77
  get version(): number;
95
78
  private inputStatus;
@@ -105,112 +88,18 @@ export declare class Transaction {
105
88
  get hash(): string;
106
89
  get id(): string;
107
90
  private checkInputIdx;
108
- getInput(idx: number): psbt.PSBTKeyMapKeys<{
109
- readonly nonWitnessUtxo: readonly [0, false, P.CoderType<P.StructInput<{
110
- version: number;
111
- segwitFlag: boolean | undefined;
112
- inputs: P.StructInput<{
113
- txid: any;
114
- index: any;
115
- finalScriptSig: any;
116
- sequence: any;
117
- }>[];
118
- outputs: P.StructInput<{
119
- amount: any;
120
- script: any;
121
- }>[];
122
- witnesses: P.Option<Uint8Array[][]>;
123
- lockTime: number;
124
- }>>, readonly [], readonly [0, 2], false];
125
- readonly witnessUtxo: readonly [1, false, P.CoderType<P.StructInput<{
126
- amount: bigint;
127
- script: Uint8Array;
128
- }>>, readonly [], readonly [0, 2], false];
129
- readonly partialSig: readonly [2, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
130
- readonly sighashType: readonly [3, false, P.CoderType<number>, readonly [], readonly [0, 2], false];
131
- readonly redeemScript: readonly [4, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
132
- readonly witnessScript: readonly [5, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
133
- readonly bip32Derivation: readonly [6, P.CoderType<Uint8Array>, P.CoderType<P.StructInput<{
134
- fingerprint: number;
135
- path: number[];
136
- }>>, readonly [], readonly [0, 2], false];
137
- readonly finalScriptSig: readonly [7, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
138
- readonly finalScriptWitness: readonly [8, false, P.CoderType<Uint8Array[]>, readonly [], readonly [0, 2], false];
139
- readonly porCommitment: readonly [9, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
140
- readonly ripemd160: readonly [10, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
141
- readonly sha256: readonly [11, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
142
- readonly hash160: readonly [12, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
143
- readonly hash256: readonly [13, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
144
- readonly txid: readonly [14, false, P.CoderType<Uint8Array>, readonly [2], readonly [2], true];
145
- readonly index: readonly [15, false, P.CoderType<number>, readonly [2], readonly [2], true];
146
- readonly sequence: readonly [16, false, P.CoderType<number>, readonly [], readonly [2], true];
147
- readonly requiredTimeLocktime: readonly [17, false, P.CoderType<number>, readonly [], readonly [2], false];
148
- readonly requiredHeightLocktime: readonly [18, false, P.CoderType<number>, readonly [], readonly [2], false];
149
- readonly tapKeySig: readonly [19, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
150
- readonly tapScriptSig: readonly [20, P.CoderType<P.StructInput<{
151
- pubKey: Uint8Array;
152
- leafHash: Uint8Array;
153
- }>>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
154
- readonly tapLeafScript: readonly [21, P.CoderType<P.StructInput<{
155
- version: number;
156
- internalKey: Uint8Array;
157
- merklePath: Uint8Array[];
158
- }>>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
159
- readonly tapBip32Derivation: readonly [22, P.CoderType<Uint8Array>, P.CoderType<P.StructInput<{
160
- hashes: Uint8Array[];
161
- der: P.StructInput<{
162
- fingerprint: any;
163
- path: any;
164
- }>;
165
- }>>, readonly [], readonly [0, 2], false];
166
- readonly tapInternalKey: readonly [23, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
167
- readonly tapMerkleRoot: readonly [24, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
168
- readonly proprietary: readonly [252, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
169
- }>;
91
+ getInput(idx: number): psbt.TransactionInput;
170
92
  get inputsLength(): number;
171
93
  addInput(input: psbt.TransactionInputUpdate, _ignoreSignStatus?: boolean): number;
172
94
  updateInput(idx: number, input: psbt.TransactionInputUpdate, _ignoreSignStatus?: boolean): void;
173
95
  private checkOutputIdx;
174
- getOutput(idx: number): psbt.PSBTKeyMapKeys<{
175
- readonly redeemScript: readonly [0, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
176
- readonly witnessScript: readonly [1, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
177
- readonly bip32Derivation: readonly [2, P.CoderType<Uint8Array>, P.CoderType<P.StructInput<{
178
- fingerprint: number;
179
- path: number[];
180
- }>>, readonly [], readonly [0, 2], false];
181
- readonly amount: readonly [3, false, P.CoderType<bigint>, readonly [2], readonly [2], true];
182
- readonly script: readonly [4, false, P.CoderType<Uint8Array>, readonly [2], readonly [2], true];
183
- readonly tapInternalKey: readonly [5, false, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
184
- readonly tapTree: readonly [6, false, P.CoderType<P.StructInput<{
185
- depth: number;
186
- version: number;
187
- script: Uint8Array;
188
- }>[]>, readonly [], readonly [0, 2], false];
189
- readonly tapBip32Derivation: readonly [7, P.CoderType<Uint8Array>, P.CoderType<P.StructInput<{
190
- hashes: Uint8Array[];
191
- der: P.StructInput<{
192
- fingerprint: any;
193
- path: any;
194
- }>;
195
- }>>, readonly [], readonly [0, 2], false];
196
- readonly proprietary: readonly [252, P.CoderType<Uint8Array>, P.CoderType<Uint8Array>, readonly [], readonly [0, 2], false];
197
- }>;
198
- getOutputAddress(idx: number, network?: {
199
- bech32: string;
200
- pubKeyHash: number;
201
- scriptHash: number;
202
- wif: number;
203
- }): string | undefined;
96
+ getOutput(idx: number): psbt.TransactionOutput;
97
+ getOutputAddress(idx: number, network?: u.BTC_NETWORK): string | undefined;
204
98
  get outputsLength(): number;
205
99
  private normalizeOutput;
206
100
  addOutput(o: psbt.TransactionOutputUpdate, _ignoreSignStatus?: boolean): number;
207
101
  updateOutput(idx: number, output: psbt.TransactionOutputUpdate, _ignoreSignStatus?: boolean): void;
208
- addOutputAddress(address: string, amount: bigint, network?: {
209
- bech32: string;
210
- pubKeyHash: number;
211
- scriptHash: number;
212
- wif: number;
213
- }): number;
102
+ addOutputAddress(address: string, amount: bigint, network?: u.BTC_NETWORK): number;
214
103
  get fee(): bigint;
215
104
  private preimageLegacy;
216
105
  preimageWitnessV0(idx: number, prevOutScript: Bytes, hashType: number, amount: bigint): Uint8Array;