@btc-vision/transaction 1.1.2 → 1.1.4

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 (34) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
  4. package/browser/transaction/browser/types/Unisat.d.ts +8 -4
  5. package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -0
  6. package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
  7. package/browser/transaction/builders/TransactionBuilder.d.ts +2 -1
  8. package/browser/transaction/shared/TweakedTransaction.d.ts +6 -3
  9. package/browser/utils/BitcoinUtils.d.ts +1 -1
  10. package/build/_version.d.ts +1 -1
  11. package/build/_version.js +1 -1
  12. package/build/buffer/BinaryWriter.js +0 -1
  13. package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
  14. package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
  15. package/build/transaction/browser/types/Unisat.d.ts +8 -4
  16. package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
  17. package/build/transaction/builders/DeploymentTransaction.js +17 -0
  18. package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
  19. package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
  20. package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
  21. package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
  22. package/build/transaction/shared/TweakedTransaction.js +61 -43
  23. package/build/utils/BitcoinUtils.d.ts +1 -1
  24. package/build/utils/BitcoinUtils.js +6 -3
  25. package/package.json +1 -1
  26. package/src/_version.ts +1 -1
  27. package/src/buffer/BinaryWriter.ts +0 -2
  28. package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
  29. package/src/transaction/browser/types/Unisat.ts +12 -4
  30. package/src/transaction/builders/DeploymentTransaction.ts +25 -0
  31. package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
  32. package/src/transaction/builders/TransactionBuilder.ts +2 -1
  33. package/src/transaction/shared/TweakedTransaction.ts +90 -49
  34. package/src/utils/BitcoinUtils.ts +12 -3
@@ -24,9 +24,10 @@ import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificat
24
24
  import { ChainId } from '../../network/ChainId.js';
25
25
  import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
26
26
  import * as bscript from '@btc-vision/bitcoin/src/script.js';
27
+ import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
27
28
 
28
29
  export interface ITweakedTransactionData {
29
- readonly signer: Signer | ECPairInterface;
30
+ readonly signer: Signer | ECPairInterface | UnisatSigner;
30
31
  readonly network: Network;
31
32
  readonly chainId?: ChainId;
32
33
  readonly nonWitnessUtxo?: Buffer;
@@ -46,22 +47,27 @@ export enum TransactionSequence {
46
47
  export abstract class TweakedTransaction extends Logger {
47
48
  public readonly logColor: string = '#00ffe1';
48
49
  public finalized: boolean = false;
50
+
49
51
  /**
50
52
  * @description Was the transaction signed?
51
53
  */
52
- protected signer: Signer | ECPairInterface;
54
+ protected signer: Signer | ECPairInterface | UnisatSigner;
55
+
53
56
  /**
54
57
  * @description Tweaked signer
55
58
  */
56
59
  protected tweakedSigner?: ECPairInterface;
60
+
57
61
  /**
58
62
  * @description The network of the transaction
59
63
  */
60
64
  protected network: Network;
65
+
61
66
  /**
62
67
  * @description Was the transaction signed?
63
68
  */
64
69
  protected signed: boolean = false;
70
+
65
71
  /**
66
72
  * @description The transaction
67
73
  * @protected
@@ -363,6 +369,7 @@ export abstract class TweakedTransaction extends Logger {
363
369
  * @param {PsbtInput} input - The input to sign
364
370
  * @param {number} i - The index of the input
365
371
  * @param {Signer} signer - The signer to use
372
+ * @param {boolean} [reverse=false] - Should the input be signed in reverse
366
373
  * @protected
367
374
  */
368
375
  protected async signInput(
@@ -370,45 +377,27 @@ export abstract class TweakedTransaction extends Logger {
370
377
  input: PsbtInput,
371
378
  i: number,
372
379
  signer: Signer | ECPairInterface,
380
+ reverse: boolean = false,
373
381
  ): Promise<void> {
374
382
  const publicKey = signer.publicKey;
375
- const isTaproot = this.isTaprootInput(input);
383
+ let isTaproot = this.isTaprootInput(input);
376
384
 
377
- let signed = false;
385
+ if (reverse) {
386
+ isTaproot = !isTaproot;
387
+ }
378
388
 
379
- if (isTaproot) {
380
- const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
389
+ let signed: boolean = false;
381
390
 
382
- if (isScriptSpend) {
383
- try {
384
- await this.signTaprootInput(signer, transaction, i);
385
- signed = true;
386
- } catch (e) {
387
- this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
388
- }
389
- } else {
390
- let tweakedSigner: ECPairInterface | undefined;
391
- if (signer !== this.signer) {
392
- tweakedSigner = this.getTweakedSigner(true, signer);
393
- } else {
394
- if (!this.tweakedSigner) this.tweakSigner();
395
- tweakedSigner = this.tweakedSigner;
396
- }
397
-
398
- if (tweakedSigner) {
399
- try {
400
- await this.signTaprootInput(tweakedSigner, transaction, i);
401
- signed = true;
402
- } catch (e) {
403
- this.error(`Failed to sign Taproot key path input ${i}: ${e}`);
404
- }
405
- } else {
406
- this.error(`Failed to obtain tweaked signer for input ${i}.`);
407
- }
391
+ if (isTaproot) {
392
+ try {
393
+ await this.attemptSignTaproot(transaction, input, i, signer, publicKey);
394
+ signed = true;
395
+ } catch (e) {
396
+ this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
408
397
  }
409
398
  } else {
410
399
  // Non-Taproot input
411
- if (this.canSignNonTaprootInput(input, publicKey)) {
400
+ if (!reverse ? this.canSignNonTaprootInput(input, publicKey) : true) {
412
401
  try {
413
402
  await this.signNonTaprootInput(signer, transaction, i);
414
403
  signed = true;
@@ -419,7 +408,11 @@ export abstract class TweakedTransaction extends Logger {
419
408
  }
420
409
 
421
410
  if (!signed) {
422
- throw new Error(`Cannot sign input ${i} with the provided signer.`);
411
+ try {
412
+ await this.signInput(transaction, input, i, signer, true);
413
+ } catch {
414
+ throw new Error(`Cannot sign input ${i} with the provided signer.`);
415
+ }
423
416
  }
424
417
  }
425
418
 
@@ -443,6 +436,12 @@ export abstract class TweakedTransaction extends Logger {
443
436
  * @returns {Promise<void>}
444
437
  */
445
438
  protected async signInputs(transaction: Psbt): Promise<void> {
439
+ if ('multiSignPsbt' in this.signer) {
440
+ await this.signInputsWalletBased(transaction);
441
+ return;
442
+ }
443
+
444
+ // non web based signing.
446
445
  const txs: PsbtInput[] = transaction.data.inputs;
447
446
 
448
447
  const batchSize: number = 20;
@@ -467,15 +466,11 @@ export abstract class TweakedTransaction extends Logger {
467
466
  await Promise.all(promises);
468
467
  }
469
468
 
470
- transaction.finalizeInput(0, this.customFinalizerP2SH);
471
-
472
- try {
473
- transaction.finalizeAllInputs();
474
-
475
- this.finalized = true;
476
- } catch (e) {
477
- this.finalized = false;
469
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
470
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
478
471
  }
472
+
473
+ this.finalized = true;
479
474
  }
480
475
 
481
476
  /**
@@ -706,6 +701,48 @@ export abstract class TweakedTransaction extends Logger {
706
701
  return getFinalScripts(inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH);
707
702
  };
708
703
 
704
+ protected async signInputsWalletBased(transaction: Psbt): Promise<void> {
705
+ const signer: UnisatSigner = this.signer as UnisatSigner;
706
+
707
+ // then, we sign all the remaining inputs with the wallet signer.
708
+ await signer.multiSignPsbt([transaction]);
709
+
710
+ // Then, we finalize every input.
711
+ for (let i = 0; i < transaction.data.inputs.length; i++) {
712
+ transaction.finalizeInput(i, this.customFinalizerP2SH);
713
+ }
714
+
715
+ this.finalized = true;
716
+ }
717
+
718
+ private async attemptSignTaproot(
719
+ transaction: Psbt,
720
+ input: PsbtInput,
721
+ i: number,
722
+ signer: Signer | ECPairInterface,
723
+ publicKey: Buffer,
724
+ ): Promise<void> {
725
+ const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
726
+
727
+ if (isScriptSpend) {
728
+ await this.signTaprootInput(signer, transaction, i);
729
+ } else {
730
+ let tweakedSigner: ECPairInterface | undefined;
731
+ if (signer !== this.signer) {
732
+ tweakedSigner = this.getTweakedSigner(true, signer);
733
+ } else {
734
+ if (!this.tweakedSigner) this.tweakSigner();
735
+ tweakedSigner = this.tweakedSigner;
736
+ }
737
+
738
+ if (tweakedSigner) {
739
+ await this.signTaprootInput(tweakedSigner, transaction, i);
740
+ } else {
741
+ this.error(`Failed to obtain tweaked signer for input ${i}.`);
742
+ }
743
+ }
744
+ }
745
+
709
746
  private isTaprootScriptSpend(input: PsbtInput, publicKey: Buffer): boolean {
710
747
  if (input.tapLeafScript && input.tapLeafScript.length > 0) {
711
748
  // Check if the signer's public key is involved in any tapLeafScript
@@ -788,13 +825,17 @@ export abstract class TweakedTransaction extends Logger {
788
825
  tapLeafHash?: Buffer,
789
826
  ): Promise<void> {
790
827
  if ('signTaprootInput' in signer) {
791
- await (
792
- signer.signTaprootInput as (
793
- tx: Psbt,
794
- i: number,
795
- tapLeafHash?: Buffer,
796
- ) => Promise<void>
797
- )(transaction, i, tapLeafHash);
828
+ try {
829
+ await (
830
+ signer.signTaprootInput as (
831
+ tx: Psbt,
832
+ i: number,
833
+ tapLeafHash?: Buffer,
834
+ ) => Promise<void>
835
+ )(transaction, i, tapLeafHash);
836
+ } catch {
837
+ throw new Error('Failed to sign Taproot input with provided signer.');
838
+ }
798
839
  } else {
799
840
  transaction.signTaprootInput(i, signer); //tapLeafHash
800
841
  }
@@ -19,13 +19,17 @@ export class BitcoinUtils {
19
19
  * @returns {Buffer} The random bytes
20
20
  */
21
21
  public static rndBytes(): Buffer {
22
- const buf = BitcoinUtils.getUnsafeRandomValues(64);
22
+ const buf = BitcoinUtils.getSafeRandomValues(64);
23
23
 
24
24
  return Buffer.from(buf);
25
25
  }
26
26
 
27
- public static getUnsafeRandomValues(length: number): Buffer {
28
- if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
27
+ public static getSafeRandomValues(length: number): Buffer {
28
+ if (
29
+ typeof globalThis.window !== 'undefined' &&
30
+ globalThis.window.crypto &&
31
+ typeof globalThis.window.crypto.getRandomValues === 'function'
32
+ ) {
29
33
  const array = new Uint8Array(length);
30
34
  window.crypto.getRandomValues(array);
31
35
 
@@ -36,6 +40,11 @@ export class BitcoinUtils {
36
40
 
37
41
  return Buffer.from(array);
38
42
  } else {
43
+ console.log(
44
+ `No secure random number generator available. Please upgrade your environment.`,
45
+ globalThis.window.crypto,
46
+ crypto,
47
+ );
39
48
  throw new Error(
40
49
  'No secure random number generator available. Please upgrade your environment.',
41
50
  );