@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.
- package/browser/_version.d.ts +1 -1
- package/browser/index.js +1 -1
- package/browser/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/browser/transaction/browser/types/Unisat.d.ts +8 -4
- package/browser/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/browser/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/browser/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/browser/utils/BitcoinUtils.d.ts +1 -1
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryWriter.js +0 -1
- package/build/transaction/browser/extensions/UnisatSigner.d.ts +4 -4
- package/build/transaction/browser/extensions/UnisatSigner.js +103 -20
- package/build/transaction/browser/types/Unisat.d.ts +8 -4
- package/build/transaction/builders/DeploymentTransaction.d.ts +1 -0
- package/build/transaction/builders/DeploymentTransaction.js +17 -0
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -0
- package/build/transaction/builders/SharedInteractionTransaction.js +31 -10
- package/build/transaction/builders/TransactionBuilder.d.ts +2 -1
- package/build/transaction/shared/TweakedTransaction.d.ts +6 -3
- package/build/transaction/shared/TweakedTransaction.js +61 -43
- package/build/utils/BitcoinUtils.d.ts +1 -1
- package/build/utils/BitcoinUtils.js +6 -3
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryWriter.ts +0 -2
- package/src/transaction/browser/extensions/UnisatSigner.ts +139 -28
- package/src/transaction/browser/types/Unisat.ts +12 -4
- package/src/transaction/builders/DeploymentTransaction.ts +25 -0
- package/src/transaction/builders/SharedInteractionTransaction.ts +51 -21
- package/src/transaction/builders/TransactionBuilder.ts +2 -1
- package/src/transaction/shared/TweakedTransaction.ts +90 -49
- 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
|
-
|
|
383
|
+
let isTaproot = this.isTaprootInput(input);
|
|
376
384
|
|
|
377
|
-
|
|
385
|
+
if (reverse) {
|
|
386
|
+
isTaproot = !isTaproot;
|
|
387
|
+
}
|
|
378
388
|
|
|
379
|
-
|
|
380
|
-
const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
|
|
389
|
+
let signed: boolean = false;
|
|
381
390
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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.
|
|
22
|
+
const buf = BitcoinUtils.getSafeRandomValues(64);
|
|
23
23
|
|
|
24
24
|
return Buffer.from(buf);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
public static
|
|
28
|
-
if (
|
|
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
|
);
|