@scure/btc-signer 1.6.0 → 1.8.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 (76) hide show
  1. package/README.md +194 -25
  2. package/esm/_type_test.js +1 -1
  3. package/esm/index.d.ts +12 -12
  4. package/esm/index.d.ts.map +1 -1
  5. package/esm/index.js +10 -10
  6. package/esm/index.js.map +1 -1
  7. package/esm/musig2.d.ts +148 -0
  8. package/esm/musig2.d.ts.map +1 -0
  9. package/esm/musig2.js +405 -0
  10. package/esm/musig2.js.map +1 -0
  11. package/esm/p2p.d.ts +38 -0
  12. package/esm/p2p.d.ts.map +1 -0
  13. package/esm/p2p.js +156 -0
  14. package/esm/p2p.js.map +1 -0
  15. package/esm/payment.d.ts +77 -21
  16. package/esm/payment.d.ts.map +1 -1
  17. package/esm/payment.js +63 -60
  18. package/esm/payment.js.map +1 -1
  19. package/esm/psbt.d.ts +463 -463
  20. package/esm/psbt.d.ts.map +1 -1
  21. package/esm/psbt.js +10 -39
  22. package/esm/psbt.js.map +1 -1
  23. package/esm/script.d.ts +16 -16
  24. package/esm/script.d.ts.map +1 -1
  25. package/esm/script.js +1 -1
  26. package/esm/transaction.d.ts +59 -6
  27. package/esm/transaction.d.ts.map +1 -1
  28. package/esm/transaction.js +154 -11
  29. package/esm/transaction.js.map +1 -1
  30. package/esm/utils.d.ts.map +1 -1
  31. package/esm/utxo.d.ts +73 -125
  32. package/esm/utxo.d.ts.map +1 -1
  33. package/esm/utxo.js +17 -122
  34. package/esm/utxo.js.map +1 -1
  35. package/index.d.ts +12 -12
  36. package/index.d.ts.map +1 -1
  37. package/index.js +58 -58
  38. package/index.js.map +1 -1
  39. package/musig2.d.ts +148 -0
  40. package/musig2.d.ts.map +1 -0
  41. package/musig2.js +417 -0
  42. package/musig2.js.map +1 -0
  43. package/p2p.d.ts +38 -0
  44. package/p2p.d.ts.map +1 -0
  45. package/p2p.js +158 -0
  46. package/p2p.js.map +1 -0
  47. package/package.json +15 -11
  48. package/payment.d.ts +77 -21
  49. package/payment.d.ts.map +1 -1
  50. package/payment.js +81 -78
  51. package/payment.js.map +1 -1
  52. package/psbt.d.ts +463 -463
  53. package/psbt.d.ts.map +1 -1
  54. package/psbt.js +30 -59
  55. package/psbt.js.map +1 -1
  56. package/script.d.ts +16 -16
  57. package/script.d.ts.map +1 -1
  58. package/script.js +3 -3
  59. package/src/_type_test.ts +1 -1
  60. package/src/index.ts +12 -12
  61. package/src/musig2.ts +517 -0
  62. package/src/p2p.ts +158 -0
  63. package/src/payment.ts +160 -75
  64. package/src/psbt.ts +11 -39
  65. package/src/script.ts +1 -1
  66. package/src/transaction.ts +170 -17
  67. package/src/utxo.ts +29 -122
  68. package/transaction.d.ts +59 -6
  69. package/transaction.d.ts.map +1 -1
  70. package/transaction.js +224 -77
  71. package/transaction.js.map +1 -1
  72. package/utils.d.ts.map +1 -1
  73. package/utxo.d.ts +73 -125
  74. package/utxo.d.ts.map +1 -1
  75. package/utxo.js +54 -163
  76. package/utxo.js.map +1 -1
package/src/payment.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { bech32, bech32m, type Coder, createBase58check, hex } from '@scure/base';
2
2
  import * as P from 'micro-packed';
3
- import { TaprootControlBlock, type TransactionInput } from './psbt.js';
4
- import { OpToNum, Script, type ScriptType, VarBytes } from './script.js';
5
- import * as u from './utils.js';
6
- import { type BTC_NETWORK, type Bytes, NETWORK } from './utils.js';
3
+ import { TaprootControlBlock, type TransactionInput } from './psbt.ts';
4
+ import { OpToNum, Script, type ScriptType, VarBytes } from './script.ts';
5
+ import * as u from './utils.ts';
6
+ import { type BTC_NETWORK, type Bytes, NETWORK } from './utils.ts';
7
7
 
8
8
  // We need following items:
9
9
  // - encode/decode output script
@@ -16,6 +16,7 @@ export type P2Ret = {
16
16
  address?: string;
17
17
  redeemScript?: Bytes;
18
18
  witnessScript?: Bytes;
19
+ hash?: Bytes;
19
20
  };
20
21
 
21
22
  // Pay to Anchor (P2A)
@@ -328,42 +329,79 @@ function uniqPubkey(pubkeys: Bytes[]) {
328
329
  map[key] = true;
329
330
  }
330
331
  }
332
+ // We want narrow types inside p2* methods, but always want to type-check if they compatible with P2Ret here!
333
+ // Also we use satisfies for additional check (ts 4.9+)
334
+ type Extends<T, U> = T extends U ? T : never;
331
335
 
332
- // @ts-ignore
333
- export const p2pk = (pubkey: Bytes, network: BTC_NETWORK = NETWORK): P2Ret => {
336
+ export type P2PK = { type: 'pk'; script: Bytes };
337
+ export const p2pk = (pubkey: Bytes, _network: BTC_NETWORK = NETWORK): Extends<P2PK, P2Ret> => {
334
338
  // network is unused
335
339
  if (!isValidPubkey(pubkey, u.PubT.ecdsa)) throw new Error('P2PK: invalid publicKey');
336
- return {
337
- type: 'pk',
338
- script: OutScript.encode({ type: 'pk', pubkey }),
339
- };
340
+ return { type: 'pk', script: OutScript.encode({ type: 'pk', pubkey }) } as const satisfies P2Ret;
340
341
  };
341
- export const p2pkh = (publicKey: Bytes, network: BTC_NETWORK = NETWORK): P2Ret => {
342
+
343
+ export type P2PKH = { type: 'pkh'; script: Bytes; address: string; hash: Bytes };
344
+ export const p2pkh = (publicKey: Bytes, network: BTC_NETWORK = NETWORK): Extends<P2PKH, P2Ret> => {
342
345
  if (!isValidPubkey(publicKey, u.PubT.ecdsa)) throw new Error('P2PKH: invalid publicKey');
343
346
  const hash = u.hash160(publicKey);
344
347
  return {
345
348
  type: 'pkh',
346
349
  script: OutScript.encode({ type: 'pkh', hash }),
347
350
  address: Address(network).encode({ type: 'pkh', hash }),
348
- };
351
+ hash,
352
+ } as const satisfies P2Ret;
349
353
  };
350
- export const p2sh = (child: P2Ret, network: BTC_NETWORK = NETWORK): P2Ret => {
354
+
355
+ export type P2SHBase = {
356
+ type: 'sh';
357
+ redeemScript: Bytes;
358
+ script: Bytes;
359
+ address: string;
360
+ hash: Bytes;
361
+ };
362
+ export type P2SHWithWitness = P2SHBase & { witnessScript: Bytes };
363
+ export type P2SHWithoutWitness = Omit<P2SHBase, 'witnessScript'>;
364
+ export type P2SHReturn<T extends P2Ret> = T extends { witnessScript: Bytes }
365
+ ? P2SHWithWitness
366
+ : P2SHWithoutWitness;
367
+ export const p2sh = <T extends P2Ret>(
368
+ child: T,
369
+ network: BTC_NETWORK = NETWORK
370
+ ): Extends<P2SHReturn<T>, P2Ret> => {
351
371
  // It is already tested inside noble-hashes and checkScript
352
372
  const cs = child.script;
353
373
  if (!u.isBytes(cs)) throw new Error(`Wrong script: ${typeof child.script}, expected Uint8Array`);
354
374
  const hash = u.hash160(cs);
355
375
  const script = OutScript.encode({ type: 'sh', hash });
356
376
  checkScript(script, cs, child.witnessScript);
357
- const res: P2Ret = {
358
- type: 'sh',
359
- redeemScript: cs,
360
- script: OutScript.encode({ type: 'sh', hash }),
361
- address: Address(network).encode({ type: 'sh', hash }),
362
- };
363
- if (child.witnessScript) res.witnessScript = child.witnessScript;
364
- return res;
377
+ if (child.witnessScript) {
378
+ return {
379
+ type: 'sh',
380
+ redeemScript: cs,
381
+ script: OutScript.encode({ type: 'sh', hash }),
382
+ address: Address(network).encode({ type: 'sh', hash }),
383
+ hash,
384
+ witnessScript: child.witnessScript,
385
+ } as Extends<P2SHReturn<T>, P2Ret> satisfies P2Ret;
386
+ } else {
387
+ return {
388
+ type: 'sh',
389
+ redeemScript: cs,
390
+ script: OutScript.encode({ type: 'sh', hash }),
391
+ address: Address(network).encode({ type: 'sh', hash }),
392
+ hash,
393
+ } as Extends<P2SHReturn<T>, P2Ret> satisfies P2Ret;
394
+ }
365
395
  };
366
- export const p2wsh = (child: P2Ret, network: BTC_NETWORK = NETWORK): P2Ret => {
396
+
397
+ export type P2WSH = {
398
+ type: 'wsh';
399
+ witnessScript: Bytes;
400
+ script: Bytes;
401
+ address: string;
402
+ hash: Bytes;
403
+ };
404
+ export const p2wsh = (child: P2Ret, network: BTC_NETWORK = NETWORK): Extends<P2WSH, P2Ret> => {
367
405
  const cs = child.script;
368
406
  if (!u.isBytes(cs)) throw new Error(`Wrong script: ${typeof cs}, expected Uint8Array`);
369
407
  const hash = u.sha256(cs);
@@ -374,9 +412,15 @@ export const p2wsh = (child: P2Ret, network: BTC_NETWORK = NETWORK): P2Ret => {
374
412
  witnessScript: cs,
375
413
  script: OutScript.encode({ type: 'wsh', hash }),
376
414
  address: Address(network).encode({ type: 'wsh', hash }),
377
- };
415
+ hash,
416
+ } as const satisfies P2Ret;
378
417
  };
379
- export const p2wpkh = (publicKey: Bytes, network: BTC_NETWORK = NETWORK): P2Ret => {
418
+
419
+ export type P2WPKH = { type: 'wpkh'; script: Bytes; address: string; hash: Bytes };
420
+ export const p2wpkh = (
421
+ publicKey: Bytes,
422
+ network: BTC_NETWORK = NETWORK
423
+ ): Extends<P2WPKH, P2Ret> => {
380
424
  if (!isValidPubkey(publicKey, u.PubT.ecdsa)) throw new Error('P2WPKH: invalid publicKey');
381
425
  if (publicKey.length === 65) throw new Error('P2WPKH: uncompressed public key');
382
426
  const hash = u.hash160(publicKey);
@@ -384,11 +428,21 @@ export const p2wpkh = (publicKey: Bytes, network: BTC_NETWORK = NETWORK): P2Ret
384
428
  type: 'wpkh',
385
429
  script: OutScript.encode({ type: 'wpkh', hash }),
386
430
  address: Address(network).encode({ type: 'wpkh', hash }),
387
- };
431
+ hash,
432
+ } as const satisfies P2Ret;
388
433
  };
389
- export const p2ms = (m: number, pubkeys: Bytes[], allowSamePubkeys = false): P2Ret => {
434
+
435
+ export type P2MS = { type: 'ms'; script: Bytes };
436
+ export const p2ms = (
437
+ m: number,
438
+ pubkeys: Bytes[],
439
+ allowSamePubkeys = false
440
+ ): Extends<P2MS, P2Ret> => {
390
441
  if (!allowSamePubkeys) uniqPubkey(pubkeys);
391
- return { type: 'ms', script: OutScript.encode({ type: 'ms', pubkeys, m }) };
442
+ return {
443
+ type: 'ms',
444
+ script: OutScript.encode({ type: 'ms', pubkeys, m }),
445
+ } as const satisfies P2Ret;
392
446
  };
393
447
 
394
448
  export type HashedTree =
@@ -438,19 +492,24 @@ function checkTaprootScript(
438
492
  }
439
493
  }
440
494
 
441
- export type P2TROut = P2Ret & {
495
+ export type P2TR = {
496
+ type: 'tr';
497
+ script: Bytes;
498
+ address: string;
442
499
  tweakedPubkey: Bytes;
443
500
  tapInternalKey: Bytes;
444
- tapMerkleRoot?: Bytes;
445
- tapLeafScript?: TransactionInput['tapLeafScript'];
446
- leaves?: TaprootLeaf[];
501
+ };
502
+ export type P2TR_TREE = P2TR & {
503
+ tapMerkleRoot: Bytes;
504
+ tapLeafScript: TransactionInput['tapLeafScript'];
505
+ leaves: TaprootLeaf[];
447
506
  };
448
507
 
449
508
  export type TaprootNode = {
450
509
  script: Bytes | string;
451
510
  leafVersion?: number;
452
511
  weight?: number;
453
- } & Partial<P2TROut>;
512
+ } & Partial<P2TR_TREE>;
454
513
  export type TaprootScriptTree = TaprootNode | TaprootScriptTree[];
455
514
  export type TaprootScriptList = TaprootNode[];
456
515
  type _TaprootTreeInternal = {
@@ -562,13 +621,28 @@ export const tapLeafHash = (script: Bytes, version: number = TAP_LEAF_VERSION):
562
621
  // Works as key OR tree.
563
622
  // If we only have tree, need to add unspendable key, otherwise
564
623
  // complex multisig wallet can be spent by owner of key only. See TAPROOT_UNSPENDABLE_KEY
624
+ export type P2TRRet<T> = T extends TaprootScriptTree ? P2TR_TREE : P2TR;
625
+ export function p2tr(
626
+ internalPubKey: Bytes | string,
627
+ tree?: undefined,
628
+ network?: BTC_NETWORK,
629
+ allowUnknownOutputs?: boolean,
630
+ customScripts?: CustomScript[]
631
+ ): Extends<P2TR, P2Ret>;
632
+ export function p2tr(
633
+ internalPubKey: Bytes | string,
634
+ tree: TaprootScriptTree,
635
+ network?: BTC_NETWORK,
636
+ allowUnknownOutputs?: boolean,
637
+ customScripts?: CustomScript[]
638
+ ): Extends<P2TR_TREE, P2Ret>;
565
639
  export function p2tr(
566
640
  internalPubKey?: Bytes | string,
567
641
  tree?: TaprootScriptTree,
568
642
  network: BTC_NETWORK = NETWORK,
569
643
  allowUnknownOutputs = false,
570
644
  customScripts?: CustomScript[]
571
- ): P2TROut {
645
+ ): Extends<P2TR & Partial<P2TR_TREE>, P2Ret> {
572
646
  // Unspendable
573
647
  if (!internalPubKey && !tree) throw new Error('p2tr: should have pubKey or scriptTree (or both)');
574
648
  const pubKey =
@@ -576,14 +650,13 @@ export function p2tr(
576
650
  ? hex.decode(internalPubKey)
577
651
  : internalPubKey || u.TAPROOT_UNSPENDABLE_KEY;
578
652
  if (!isValidPubkey(pubKey, u.PubT.schnorr)) throw new Error('p2tr: non-schnorr pubkey');
579
- let hashedTree = tree
580
- ? taprootAddPath(taprootHashTree(tree, pubKey, allowUnknownOutputs, customScripts))
581
- : undefined;
582
- const tapMerkleRoot = hashedTree ? hashedTree.hash : undefined;
583
- const [tweakedPubkey, parity] = u.taprootTweakPubkey(pubKey, tapMerkleRoot || P.EMPTY);
584
- let leaves;
585
- if (hashedTree) {
586
- leaves = taprootWalkTree(hashedTree).map((l) => ({
653
+ if (tree) {
654
+ let hashedTree = taprootAddPath(
655
+ taprootHashTree(tree, pubKey, allowUnknownOutputs, customScripts)
656
+ );
657
+ const tapMerkleRoot = hashedTree.hash;
658
+ const [tweakedPubkey, parity] = u.taprootTweakPubkey(pubKey, tapMerkleRoot);
659
+ const leaves = taprootWalkTree(hashedTree).map((l) => ({
587
660
  ...l,
588
661
  controlBlock: TaprootControlBlock.encode({
589
662
  version: (l.version || TAP_LEAF_VERSION) + parity,
@@ -591,28 +664,33 @@ export function p2tr(
591
664
  merklePath: l.path,
592
665
  }),
593
666
  }));
667
+ return {
668
+ type: 'tr',
669
+ script: OutScript.encode({ type: 'tr', pubkey: tweakedPubkey }),
670
+ address: Address(network).encode({ type: 'tr', pubkey: tweakedPubkey }),
671
+ // For tests
672
+ tweakedPubkey,
673
+ // PSBT stuff
674
+ tapInternalKey: pubKey,
675
+ leaves,
676
+ tapLeafScript: leaves.map((l) => [
677
+ TaprootControlBlock.decode(l.controlBlock),
678
+ u.concatBytes(l.script, new Uint8Array([l.version || TAP_LEAF_VERSION])),
679
+ ]),
680
+ tapMerkleRoot,
681
+ } as const satisfies P2TR_TREE;
682
+ } else {
683
+ const tweakedPubkey = u.taprootTweakPubkey(pubKey, P.EMPTY)[0];
684
+ return {
685
+ type: 'tr',
686
+ script: OutScript.encode({ type: 'tr', pubkey: tweakedPubkey }),
687
+ address: Address(network).encode({ type: 'tr', pubkey: tweakedPubkey }),
688
+ // For tests
689
+ tweakedPubkey,
690
+ // PSBT stuff
691
+ tapInternalKey: pubKey,
692
+ } as const satisfies P2TR;
594
693
  }
595
- let tapLeafScript: TransactionInput['tapLeafScript'];
596
- if (leaves) {
597
- tapLeafScript = leaves.map((l) => [
598
- TaprootControlBlock.decode(l.controlBlock),
599
- u.concatBytes(l.script, new Uint8Array([l.version || TAP_LEAF_VERSION])),
600
- ]);
601
- }
602
- const res: P2TROut = {
603
- type: 'tr',
604
- script: OutScript.encode({ type: 'tr', pubkey: tweakedPubkey }),
605
- address: Address(network).encode({ type: 'tr', pubkey: tweakedPubkey }),
606
- // For tests
607
- tweakedPubkey,
608
- // PSBT stuff
609
- tapInternalKey: pubKey,
610
- };
611
- // Just in case someone would want to select a specific script
612
- if (leaves) res.leaves = leaves;
613
- if (tapLeafScript) res.tapLeafScript = tapLeafScript;
614
- if (tapMerkleRoot) res.tapMerkleRoot = tapMerkleRoot;
615
- return res;
616
694
  }
617
695
 
618
696
  // Returns all combinations of size M from lst
@@ -651,29 +729,37 @@ export function combinations<T>(m: number, list: T[]): T[][] {
651
729
  * Takes O(n^2) if m != n. 99-of-100 is ok, 5-of-100 is not.
652
730
  * `2-of-[A,B,C] => [A,B] | [A,C] | [B,C]`
653
731
  */
654
- export const p2tr_ns = (m: number, pubkeys: Bytes[], allowSamePubkeys = false): P2Ret[] => {
732
+ export type P2TR_NS = { type: 'tr_ns'; script: Bytes };
733
+ export const p2tr_ns = (
734
+ m: number,
735
+ pubkeys: Bytes[],
736
+ allowSamePubkeys = false
737
+ ): Extends<P2TR_NS, P2Ret>[] => {
655
738
  if (!allowSamePubkeys) uniqPubkey(pubkeys);
656
- return combinations(m, pubkeys).map((i) => ({
657
- type: 'tr_ns',
658
- script: OutScript.encode({ type: 'tr_ns', pubkeys: i }),
659
- }));
739
+ return combinations(m, pubkeys).map(
740
+ (i) =>
741
+ ({
742
+ type: 'tr_ns',
743
+ script: OutScript.encode({ type: 'tr_ns', pubkeys: i }),
744
+ }) as const
745
+ ) satisfies P2Ret[];
660
746
  };
661
747
  // Taproot public key (case of p2tr_ns)
662
- export const p2tr_pk = (pubkey: Bytes): P2Ret => p2tr_ns(1, [pubkey], undefined)[0];
748
+ export type P2TR_PK = P2TR_NS;
749
+ export const p2tr_pk = (pubkey: Bytes): Extends<P2TR_PK, P2Ret> =>
750
+ p2tr_ns(1, [pubkey], undefined)[0] satisfies P2Ret;
663
751
 
752
+ export type P2TR_MS = { type: 'tr_ms'; script: Bytes };
664
753
  export function p2tr_ms(
665
754
  m: number,
666
755
  pubkeys: Bytes[],
667
756
  allowSamePubkeys = false
668
- ): {
669
- type: string;
670
- script: Uint8Array;
671
- } {
757
+ ): Extends<P2TR_MS, P2Ret> {
672
758
  if (!allowSamePubkeys) uniqPubkey(pubkeys);
673
759
  return {
674
760
  type: 'tr_ms',
675
761
  script: OutScript.encode({ type: 'tr_ms', pubkeys, m }),
676
- };
762
+ } as const satisfies P2Ret;
677
763
  }
678
764
 
679
765
  // Simple pubkey address, without complex scripts
@@ -691,8 +777,7 @@ export function getAddress(
691
777
  throw new Error(`getAddress: unknown type=${type}`);
692
778
  }
693
779
 
694
- export const _sortPubkeys = (pubkeys: Bytes[]): Uint8Array[] =>
695
- Array.from(pubkeys).sort(u.compareBytes);
780
+ export const _sortPubkeys = (pubkeys: Bytes[]): Bytes[] => Array.from(pubkeys).sort(u.compareBytes);
696
781
 
697
782
  export function multisig(
698
783
  m: number,
package/src/psbt.ts CHANGED
@@ -8,9 +8,8 @@ import {
8
8
  RawTx,
9
9
  RawWitness,
10
10
  VarBytes,
11
- } from './script.js';
12
- import { Transaction } from './transaction.js'; // circular
13
- import { type Bytes, compareBytes, equalBytes, PubT, validatePubkey } from './utils.js';
11
+ } from './script.ts';
12
+ import { type Bytes, compareBytes, equalBytes, PubT, validatePubkey } from './utils.ts';
14
13
 
15
14
  // PSBT BIP174, BIP370, BIP371
16
15
 
@@ -282,17 +281,6 @@ export const PSBTInputCoder = P.validate(PSBTKeyMap(PSBTInput), (i) => {
282
281
  (i.requiredHeightLocktime <= 0 || i.requiredHeightLocktime >= 500000000)
283
282
  )
284
283
  throw new Error(`validateInput: wrong heighLocktime=${i.requiredHeightLocktime}`);
285
-
286
- if (i.nonWitnessUtxo && i.index !== undefined) {
287
- const last = i.nonWitnessUtxo.outputs.length - 1;
288
- if (i.index > last) throw new Error(`validateInput: index(${i.index}) not in nonWitnessUtxo`);
289
- const prevOut = i.nonWitnessUtxo.outputs[i.index];
290
- if (
291
- i.witnessUtxo &&
292
- (!equalBytes(i.witnessUtxo.script, prevOut.script) || i.witnessUtxo.amount !== prevOut.amount)
293
- )
294
- throw new Error('validateInput: witnessUtxo different from nonWitnessUtxo');
295
- }
296
284
  if (i.tapLeafScript) {
297
285
  // tap leaf version appears here twice: in control block and at the end of script
298
286
  for (const [k, v] of i.tapLeafScript) {
@@ -302,28 +290,6 @@ export const PSBTInputCoder = P.validate(PSBTKeyMap(PSBTInput), (i) => {
302
290
  throw new Error('validateInput: tapLeafScript version has parity bit!');
303
291
  }
304
292
  }
305
- // Validate txid for nonWitnessUtxo is correct
306
- if (i.nonWitnessUtxo && i.index !== undefined && i.txid) {
307
- const outputs = i.nonWitnessUtxo.outputs;
308
- if (outputs.length - 1 < i.index) throw new Error('nonWitnessUtxo: incorect output index');
309
- // At this point, we are using previous tx output to create new input.
310
- // Script safety checks are unnecessary:
311
- // - User has no control over previous tx. If somebody send money in same tx
312
- // as unspendable output, we still want user able to spend money
313
- // - We still want some checks to notify user about possible errors early
314
- // in case user wants to use wrong input by mistake
315
- // - Worst case: tx will be rejected by nodes. Still better than disallowing user
316
- // to spend real input, no matter how broken it looks
317
- const tx = Transaction.fromRaw(RawTx.encode(i.nonWitnessUtxo), {
318
- allowUnknownOutputs: true,
319
- disableScriptCheck: true,
320
- allowUnknownInputs: true,
321
- });
322
- const txid = hex.encode(i.txid);
323
- // PSBTv2 vectors have non-final tx in inputs
324
- if (tx.isFinal && tx.id !== txid)
325
- throw new Error(`nonWitnessUtxo: wrong txid, exp=${txid} got=${tx.id}`);
326
- }
327
293
  return i;
328
294
  });
329
295
 
@@ -458,7 +424,8 @@ export function mergeKeyMap<T extends PSBTKeyMap>(
458
424
  psbtEnum: T,
459
425
  val: PSBTKeyMapKeys<T>,
460
426
  cur?: PSBTKeyMapKeys<T>,
461
- allowedFields?: (keyof PSBTKeyMapKeys<T>)[]
427
+ allowedFields?: (keyof PSBTKeyMapKeys<T>)[],
428
+ allowUnknown?: boolean
462
429
  ): PSBTKeyMapKeys<T> {
463
430
  const res: PSBTKeyMapKeys<T> = { ...cur, ...val };
464
431
  // All arguments can be provided as hex
@@ -517,8 +484,13 @@ export function mergeKeyMap<T extends PSBTKeyMap>(
517
484
  throw new Error(`Cannot change signed field=${k}`);
518
485
  }
519
486
  }
520
- // Remove unknown keys
521
- for (const k in res) if (!psbtEnum[k]) delete res[k];
487
+ // Remove unknown keys except the "unknown" array if allowUnknown is true
488
+ for (const k in res) {
489
+ if (!psbtEnum[k]) {
490
+ if (allowUnknown && k === 'unknown') continue;
491
+ delete res[k];
492
+ }
493
+ }
522
494
  return res;
523
495
  }
524
496
 
package/src/script.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as P from 'micro-packed';
2
- import { isBytes } from './utils.js';
2
+ import { isBytes } from './utils.ts';
3
3
 
4
4
  export const MAX_SCRIPT_BYTE_LENGTH = 520;
5
5