@btc-vision/transaction 1.1.16 → 1.2.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/browser/_version.d.ts +1 -1
- package/browser/crypto/crypto-browser.d.ts +2 -2
- package/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +2 -0
- package/browser/keypair/Address.d.ts +10 -0
- package/browser/keypair/Secp256k1PointDeriver.d.ts +16 -0
- package/browser/opnet.d.ts +2 -0
- package/browser/signer/SignerUtils.d.ts +5 -0
- package/browser/transaction/ContractAddress.d.ts +6 -0
- package/browser/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/browser/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/browser/transaction/builders/MultiSignTransaction.d.ts +3 -4
- package/browser/transaction/builders/SharedInteractionTransaction.d.ts +2 -3
- package/browser/transaction/shared/TweakedTransaction.d.ts +5 -6
- package/browser/utxo/interfaces/IUTXO.d.ts +1 -0
- package/browser/verification/TapscriptVerificator.d.ts +1 -2
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/buffer/BinaryReader.js +2 -2
- package/build/buffer/BinaryWriter.js +1 -1
- package/build/generators/AddressGenerator.js +1 -2
- package/build/generators/Generator.js +1 -2
- package/build/generators/builders/CalldataGenerator.js +1 -1
- package/build/generators/builders/LegacyCalldataGenerator.js +1 -1
- package/build/generators/builders/MultiSignGenerator.js +1 -2
- package/build/keypair/Address.d.ts +10 -0
- package/build/keypair/Address.js +76 -7
- package/build/keypair/AddressVerificator.js +5 -2
- package/build/keypair/EcKeyPair.js +3 -4
- package/build/keypair/MessageSigner.js +1 -2
- package/build/keypair/Secp256k1PointDeriver.d.ts +16 -0
- package/build/keypair/Secp256k1PointDeriver.js +80 -0
- package/build/keypair/Wallet.js +1 -2
- package/build/opnet.d.ts +2 -0
- package/build/opnet.js +2 -0
- package/build/signer/SignerUtils.d.ts +5 -0
- package/build/signer/SignerUtils.js +40 -0
- package/build/signer/TweakedSigner.js +1 -3
- package/build/transaction/ContractAddress.d.ts +6 -0
- package/build/transaction/ContractAddress.js +12 -0
- package/build/transaction/browser/extensions/UnisatSigner.js +5 -33
- package/build/transaction/browser/extensions/XverseSigner.js +5 -49
- package/build/transaction/builders/CustomScriptTransaction.d.ts +1 -1
- package/build/transaction/builders/CustomScriptTransaction.js +1 -2
- package/build/transaction/builders/DeploymentTransaction.js +2 -2
- package/build/transaction/builders/FundingTransaction.js +6 -1
- package/build/transaction/builders/InteractionTransaction.d.ts +1 -1
- package/build/transaction/builders/MultiSignTransaction.d.ts +3 -4
- package/build/transaction/builders/MultiSignTransaction.js +1 -2
- package/build/transaction/builders/SharedInteractionTransaction.d.ts +2 -3
- package/build/transaction/builders/SharedInteractionTransaction.js +1 -2
- package/build/transaction/builders/TransactionBuilder.js +14 -3
- package/build/transaction/shared/TweakedTransaction.d.ts +5 -6
- package/build/transaction/shared/TweakedTransaction.js +121 -94
- package/build/utils/BitcoinUtils.js +4 -4
- package/build/utils/BufferHelper.js +1 -1
- package/build/utxo/OPNetLimitedProvider.js +1 -0
- package/build/utxo/interfaces/IUTXO.d.ts +1 -0
- package/build/verification/TapscriptVerificator.d.ts +1 -2
- package/build/verification/TapscriptVerificator.js +1 -2
- package/package.json +9 -12
- package/src/_version.ts +1 -1
- package/src/buffer/BinaryReader.ts +2 -2
- package/src/buffer/BinaryWriter.ts +1 -1
- package/src/generators/AddressGenerator.ts +1 -2
- package/src/generators/Generator.ts +1 -2
- package/src/generators/builders/CalldataGenerator.ts +5 -1
- package/src/generators/builders/LegacyCalldataGenerator.ts +5 -1
- package/src/generators/builders/MultiSignGenerator.ts +1 -2
- package/src/keypair/Address.ts +106 -10
- package/src/keypair/AddressVerificator.ts +6 -2
- package/src/keypair/EcKeyPair.ts +14 -5
- package/src/keypair/MessageSigner.ts +1 -2
- package/src/keypair/Secp256k1PointDeriver.ts +170 -0
- package/src/keypair/Wallet.ts +1 -2
- package/src/opnet.ts +2 -0
- package/src/signer/SignerUtils.ts +66 -0
- package/src/signer/TweakedSigner.ts +1 -3
- package/src/transaction/ContractAddress.ts +13 -0
- package/src/transaction/TransactionFactory.ts +4 -258
- package/src/transaction/browser/extensions/UnisatSigner.ts +5 -41
- package/src/transaction/browser/extensions/XverseSigner.ts +9 -69
- package/src/transaction/builders/CustomScriptTransaction.ts +10 -3
- package/src/transaction/builders/DeploymentTransaction.ts +13 -3
- package/src/transaction/builders/FundingTransaction.ts +7 -2
- package/src/transaction/builders/InteractionTransaction.ts +1 -1
- package/src/transaction/builders/MultiSignTransaction.ts +2 -2
- package/src/transaction/builders/SharedInteractionTransaction.ts +1 -3
- package/src/transaction/builders/TransactionBuilder.ts +14 -2
- package/src/transaction/shared/TweakedTransaction.ts +186 -123
- package/src/utils/BitcoinUtils.ts +4 -4
- package/src/utils/BufferHelper.ts +1 -1
- package/src/utxo/OPNetLimitedProvider.ts +1 -0
- package/src/utxo/interfaces/IUTXO.ts +2 -0
- package/src/verification/TapscriptVerificator.ts +9 -3
|
@@ -3,6 +3,13 @@ import {
|
|
|
3
3
|
address as bitAddress,
|
|
4
4
|
crypto as bitCrypto,
|
|
5
5
|
getFinalScripts,
|
|
6
|
+
isP2MS,
|
|
7
|
+
isP2PK,
|
|
8
|
+
isP2PKH,
|
|
9
|
+
isP2SHScript,
|
|
10
|
+
isP2TR,
|
|
11
|
+
isP2WPKH,
|
|
12
|
+
isP2WSHScript,
|
|
6
13
|
Network,
|
|
7
14
|
opcodes,
|
|
8
15
|
Payment,
|
|
@@ -12,19 +19,22 @@ import {
|
|
|
12
19
|
PsbtInputExtended,
|
|
13
20
|
script,
|
|
14
21
|
Signer,
|
|
22
|
+
toXOnly,
|
|
15
23
|
Transaction,
|
|
24
|
+
varuint,
|
|
16
25
|
} from '@btc-vision/bitcoin';
|
|
17
26
|
|
|
18
27
|
import { TweakedSigner, TweakSettings } from '../../signer/TweakedSigner.js';
|
|
19
28
|
import { ECPairInterface } from 'ecpair';
|
|
20
|
-
import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
|
|
21
29
|
import { UTXO } from '../../utxo/interfaces/IUTXO.js';
|
|
22
30
|
import { TapLeafScript } from '../interfaces/Tap.js';
|
|
23
|
-
import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificator.js';
|
|
24
31
|
import { ChainId } from '../../network/ChainId.js';
|
|
25
|
-
import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
|
|
26
|
-
import * as bscript from '@btc-vision/bitcoin/src/script.js';
|
|
27
32
|
import { UnisatSigner } from '../browser/extensions/UnisatSigner.js';
|
|
33
|
+
import {
|
|
34
|
+
canSignNonTaprootInput,
|
|
35
|
+
isTaprootInput,
|
|
36
|
+
pubkeyInScript,
|
|
37
|
+
} from '../../signer/SignerUtils.js';
|
|
28
38
|
|
|
29
39
|
export interface ITweakedTransactionData {
|
|
30
40
|
readonly signer: Signer | ECPairInterface | UnisatSigner;
|
|
@@ -370,6 +380,7 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
370
380
|
* @param {number} i - The index of the input
|
|
371
381
|
* @param {Signer} signer - The signer to use
|
|
372
382
|
* @param {boolean} [reverse=false] - Should the input be signed in reverse
|
|
383
|
+
* @param {boolean} [errored=false] - Was there an error
|
|
373
384
|
* @protected
|
|
374
385
|
*/
|
|
375
386
|
protected async signInput(
|
|
@@ -378,38 +389,48 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
378
389
|
i: number,
|
|
379
390
|
signer: Signer | ECPairInterface,
|
|
380
391
|
reverse: boolean = false,
|
|
392
|
+
errored: boolean = false,
|
|
381
393
|
): Promise<void> {
|
|
382
394
|
const publicKey = signer.publicKey;
|
|
383
|
-
let isTaproot = this.isTaprootInput(input);
|
|
384
395
|
|
|
396
|
+
let isTaproot = isTaprootInput(input);
|
|
385
397
|
if (reverse) {
|
|
386
398
|
isTaproot = !isTaproot;
|
|
387
399
|
}
|
|
388
400
|
|
|
389
401
|
let signed: boolean = false;
|
|
390
|
-
|
|
402
|
+
let didError: boolean = false;
|
|
391
403
|
if (isTaproot) {
|
|
392
404
|
try {
|
|
393
405
|
await this.attemptSignTaproot(transaction, input, i, signer, publicKey);
|
|
394
406
|
signed = true;
|
|
395
407
|
} catch (e) {
|
|
396
|
-
this.error(
|
|
408
|
+
this.error(
|
|
409
|
+
`Failed to sign Taproot script path input ${i} (reverse: ${reverse}): ${(e as Error).message}`,
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
didError = true;
|
|
397
413
|
}
|
|
398
414
|
} else {
|
|
399
415
|
// Non-Taproot input
|
|
400
|
-
if (!reverse ?
|
|
416
|
+
if (!reverse ? canSignNonTaprootInput(input, publicKey) : true) {
|
|
401
417
|
try {
|
|
402
418
|
await this.signNonTaprootInput(signer, transaction, i);
|
|
403
419
|
signed = true;
|
|
404
420
|
} catch (e) {
|
|
405
|
-
this.error(`Failed to sign non-Taproot input ${i}: ${e}`);
|
|
421
|
+
this.error(`Failed to sign non-Taproot input ${i}: ${(e as Error).stack}`);
|
|
422
|
+
didError = true;
|
|
406
423
|
}
|
|
407
424
|
}
|
|
408
425
|
}
|
|
409
426
|
|
|
410
427
|
if (!signed) {
|
|
428
|
+
if (didError && errored) {
|
|
429
|
+
throw new Error(`Failed to sign input ${i} with the provided signer.`);
|
|
430
|
+
}
|
|
431
|
+
|
|
411
432
|
try {
|
|
412
|
-
await this.signInput(transaction, input, i, signer, true);
|
|
433
|
+
await this.signInput(transaction, input, i, signer, true, didError);
|
|
413
434
|
} catch {
|
|
414
435
|
throw new Error(`Cannot sign input ${i} with the provided signer.`);
|
|
415
436
|
}
|
|
@@ -585,94 +606,198 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
585
606
|
return;
|
|
586
607
|
}
|
|
587
608
|
|
|
609
|
+
protected generateP2SHP2PKHRedeemScript(inputAddr: string):
|
|
610
|
+
| {
|
|
611
|
+
redeemScript: Buffer;
|
|
612
|
+
outputScript: Buffer;
|
|
613
|
+
}
|
|
614
|
+
| undefined {
|
|
615
|
+
const pubkey = Buffer.isBuffer(this.signer.publicKey)
|
|
616
|
+
? this.signer.publicKey
|
|
617
|
+
: Buffer.from(this.signer.publicKey, 'hex');
|
|
618
|
+
|
|
619
|
+
const w = payments.p2wpkh({
|
|
620
|
+
pubkey: pubkey,
|
|
621
|
+
network: this.network,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const p = payments.p2sh({
|
|
625
|
+
redeem: w,
|
|
626
|
+
network: this.network,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
const address = p.address;
|
|
630
|
+
const redeemScript = p.redeem?.output;
|
|
631
|
+
if (!redeemScript) {
|
|
632
|
+
throw new Error('Failed to generate P2SH-P2WPKH redeem script');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (address === inputAddr && p.redeem && p.redeem.output && p.output) {
|
|
636
|
+
return {
|
|
637
|
+
redeemScript: p.redeem.output,
|
|
638
|
+
outputScript: p.output,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
588
645
|
/**
|
|
589
|
-
* Generate the PSBT input extended
|
|
646
|
+
* Generate the PSBT input extended, supporting various script types
|
|
590
647
|
* @param {UTXO} utxo The UTXO
|
|
591
648
|
* @param {number} i The index of the input
|
|
592
649
|
* @protected
|
|
593
650
|
* @returns {PsbtInputExtended} The PSBT input extended
|
|
594
651
|
*/
|
|
595
652
|
protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended {
|
|
653
|
+
const script = Buffer.from(utxo.scriptPubKey.hex, 'hex');
|
|
654
|
+
|
|
596
655
|
const input: PsbtInputExtended = {
|
|
597
656
|
hash: utxo.transactionId,
|
|
598
657
|
index: utxo.outputIndex,
|
|
599
658
|
sequence: this.sequence,
|
|
600
659
|
witnessUtxo: {
|
|
601
660
|
value: Number(utxo.value),
|
|
602
|
-
script
|
|
661
|
+
script,
|
|
603
662
|
},
|
|
604
663
|
};
|
|
605
664
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
665
|
+
// Handle P2PKH (Legacy)
|
|
666
|
+
if (isP2PKH(script)) {
|
|
667
|
+
// Legacy input requires nonWitnessUtxo
|
|
668
|
+
if (utxo.nonWitnessUtxo) {
|
|
669
|
+
//delete input.witnessUtxo;
|
|
670
|
+
input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
|
|
671
|
+
? utxo.nonWitnessUtxo
|
|
672
|
+
: Buffer.from(utxo.nonWitnessUtxo, 'hex');
|
|
673
|
+
} else {
|
|
674
|
+
throw new Error('Missing nonWitnessUtxo for P2PKH UTXO');
|
|
675
|
+
}
|
|
676
|
+
}
|
|
613
677
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
678
|
+
// Handle P2WPKH (SegWit)
|
|
679
|
+
else if (isP2WPKH(script)) {
|
|
680
|
+
// No redeemScript required for pure P2WPKH
|
|
681
|
+
// witnessUtxo is enough, no nonWitnessUtxo needed.
|
|
682
|
+
}
|
|
619
683
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
684
|
+
// Handle P2WSH (SegWit)
|
|
685
|
+
else if (isP2WSHScript(script)) {
|
|
686
|
+
// P2WSH requires a witnessScript
|
|
687
|
+
if (!utxo.witnessScript) {
|
|
688
|
+
// Can't just invent a witnessScript out of thin air. If not provided, it's an error.
|
|
689
|
+
throw new Error('Missing witnessScript for P2WSH UTXO');
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
input.witnessScript = Buffer.isBuffer(utxo.witnessScript)
|
|
693
|
+
? utxo.witnessScript
|
|
694
|
+
: Buffer.from(utxo.witnessScript, 'hex');
|
|
695
|
+
|
|
696
|
+
// No nonWitnessUtxo needed for segwit
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Handle P2SH (Can be legacy or wrapping segwit)
|
|
700
|
+
else if (isP2SHScript(script)) {
|
|
701
|
+
// Redeem script is required for P2SH
|
|
702
|
+
let redeemScriptBuf: Buffer | undefined;
|
|
623
703
|
|
|
624
|
-
|
|
625
|
-
|
|
704
|
+
if (utxo.redeemScript) {
|
|
705
|
+
redeemScriptBuf = Buffer.isBuffer(utxo.redeemScript)
|
|
706
|
+
? utxo.redeemScript
|
|
707
|
+
: Buffer.from(utxo.redeemScript, 'hex');
|
|
708
|
+
} else {
|
|
709
|
+
// Attempt to generate a redeem script if missing
|
|
710
|
+
if (!utxo.scriptPubKey.address) {
|
|
711
|
+
throw new Error(
|
|
712
|
+
'Missing redeemScript and no address to regenerate it for P2SH UTXO',
|
|
713
|
+
);
|
|
626
714
|
}
|
|
627
|
-
|
|
628
|
-
this.
|
|
715
|
+
|
|
716
|
+
const legacyScripts = this.generateP2SHP2PKHRedeemScript(utxo.scriptPubKey.address);
|
|
717
|
+
if (!legacyScripts) {
|
|
718
|
+
throw new Error('Missing redeemScript for P2SH UTXO and unable to regenerate');
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
redeemScriptBuf = legacyScripts.redeemScript;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
input.redeemScript = redeemScriptBuf;
|
|
725
|
+
|
|
726
|
+
// Check if redeemScript is wrapping segwit (like P2SH-P2WPKH or P2SH-P2WSH)
|
|
727
|
+
const payment = payments.p2sh({ redeem: { output: input.redeemScript } });
|
|
728
|
+
if (!payment.redeem) {
|
|
729
|
+
throw new Error('Failed to extract redeem script from P2SH UTXO');
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const redeemOutput = payment.redeem.output;
|
|
733
|
+
if (!redeemOutput) {
|
|
734
|
+
throw new Error('Failed to extract redeem output from P2SH UTXO');
|
|
629
735
|
}
|
|
630
|
-
}
|
|
631
736
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
737
|
+
if (utxo.nonWitnessUtxo) {
|
|
738
|
+
input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
|
|
739
|
+
? utxo.nonWitnessUtxo
|
|
740
|
+
: Buffer.from(utxo.nonWitnessUtxo, 'hex');
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (isP2WPKH(redeemOutput)) {
|
|
744
|
+
// P2SH-P2WPKH
|
|
745
|
+
// Use witnessUtxo + redeemScript
|
|
746
|
+
delete input.nonWitnessUtxo; // ensure we do NOT have nonWitnessUtxo
|
|
747
|
+
// witnessScript is not needed
|
|
748
|
+
} else if (isP2WSHScript(redeemOutput)) {
|
|
749
|
+
// P2SH-P2WSH
|
|
750
|
+
// Use witnessUtxo + redeemScript + witnessScript
|
|
751
|
+
delete input.nonWitnessUtxo; // ensure we do NOT have nonWitnessUtxo
|
|
752
|
+
if (!input.witnessScript) {
|
|
753
|
+
throw new Error('Missing witnessScript for P2SH-P2WSH UTXO');
|
|
754
|
+
}
|
|
755
|
+
} else {
|
|
756
|
+
// Legacy P2SH
|
|
757
|
+
// Use nonWitnessUtxo
|
|
758
|
+
delete input.witnessUtxo; // ensure we do NOT have witnessUtxo
|
|
759
|
+
}
|
|
637
760
|
}
|
|
638
761
|
|
|
639
|
-
//
|
|
640
|
-
if (
|
|
641
|
-
|
|
642
|
-
? utxo.redeemScript
|
|
643
|
-
: Buffer.from(utxo.redeemScript, 'hex');
|
|
762
|
+
// Handle P2TR (Taproot)
|
|
763
|
+
else if (isP2TR(script)) {
|
|
764
|
+
// Taproot inputs do not require nonWitnessUtxo, witnessUtxo is sufficient.
|
|
644
765
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
766
|
+
// If there's a configured sighash type
|
|
767
|
+
if (this.sighashTypes) {
|
|
768
|
+
const inputSign = TweakedTransaction.calculateSignHash(this.sighashTypes);
|
|
769
|
+
if (inputSign) input.sighashType = inputSign;
|
|
649
770
|
}
|
|
771
|
+
|
|
772
|
+
// Taproot internal key
|
|
773
|
+
this.tweakSigner();
|
|
774
|
+
input.tapInternalKey = this.internalPubKeyToXOnly();
|
|
650
775
|
}
|
|
651
776
|
|
|
652
|
-
//
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
if (
|
|
777
|
+
// Handle P2PK (legacy) or P2MS (bare multisig)
|
|
778
|
+
else if (isP2PK(script) || isP2MS(script)) {
|
|
779
|
+
// These are legacy scripts, need nonWitnessUtxo
|
|
780
|
+
if (utxo.nonWitnessUtxo) {
|
|
781
|
+
input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
|
|
782
|
+
? utxo.nonWitnessUtxo
|
|
783
|
+
: Buffer.from(utxo.nonWitnessUtxo, 'hex');
|
|
784
|
+
} else {
|
|
785
|
+
throw new Error('Missing nonWitnessUtxo for P2PK or P2MS UTXO');
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
this.error(`Unknown or unsupported script type for output: ${utxo.scriptPubKey.hex}`);
|
|
656
789
|
}
|
|
657
790
|
|
|
791
|
+
// TapLeafScript if available
|
|
658
792
|
if (this.tapLeafScript) {
|
|
659
793
|
input.tapLeafScript = [this.tapLeafScript];
|
|
660
794
|
}
|
|
661
795
|
|
|
796
|
+
// If the first input and we have a global nonWitnessUtxo not yet set
|
|
662
797
|
if (i === 0 && this.nonWitnessUtxo) {
|
|
663
798
|
input.nonWitnessUtxo = this.nonWitnessUtxo;
|
|
664
799
|
}
|
|
665
800
|
|
|
666
|
-
// Automatically detect P2TR inputs.
|
|
667
|
-
if (
|
|
668
|
-
utxo.scriptPubKey.address &&
|
|
669
|
-
AddressVerificator.isValidP2TRAddress(utxo.scriptPubKey.address, this.network)
|
|
670
|
-
) {
|
|
671
|
-
this.tweakSigner();
|
|
672
|
-
|
|
673
|
-
input.tapInternalKey = this.internalPubKeyToXOnly();
|
|
674
|
-
}
|
|
675
|
-
|
|
676
801
|
return input;
|
|
677
802
|
}
|
|
678
803
|
|
|
@@ -747,7 +872,7 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
747
872
|
if (input.tapLeafScript && input.tapLeafScript.length > 0) {
|
|
748
873
|
// Check if the signer's public key is involved in any tapLeafScript
|
|
749
874
|
for (const tapLeafScript of input.tapLeafScript) {
|
|
750
|
-
if (
|
|
875
|
+
if (pubkeyInScript(publicKey, tapLeafScript.script)) {
|
|
751
876
|
// The public key is in the script; it's a script spend
|
|
752
877
|
return true;
|
|
753
878
|
}
|
|
@@ -756,68 +881,6 @@ export abstract class TweakedTransaction extends Logger {
|
|
|
756
881
|
return false;
|
|
757
882
|
}
|
|
758
883
|
|
|
759
|
-
// Helper method to determine if an input is Taproot
|
|
760
|
-
private isTaprootInput(input: PsbtInput): boolean {
|
|
761
|
-
if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
|
|
762
|
-
return true;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (input.witnessUtxo) {
|
|
766
|
-
const script = input.witnessUtxo.script;
|
|
767
|
-
// Check if the script is a P2TR output (OP_1 [32-byte key])
|
|
768
|
-
return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
return false;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Check if the signer can sign the non-Taproot input
|
|
775
|
-
private canSignNonTaprootInput(input: PsbtInput, publicKey: Buffer): boolean {
|
|
776
|
-
const script = this.getInputRelevantScript(input);
|
|
777
|
-
if (script) {
|
|
778
|
-
return this.pubkeyInScript(publicKey, script);
|
|
779
|
-
}
|
|
780
|
-
return false;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Helper method to extract the relevant script from the input
|
|
784
|
-
private getInputRelevantScript(input: PsbtInput): Buffer | null {
|
|
785
|
-
if (input.redeemScript) {
|
|
786
|
-
return input.redeemScript;
|
|
787
|
-
}
|
|
788
|
-
if (input.witnessScript) {
|
|
789
|
-
return input.witnessScript;
|
|
790
|
-
}
|
|
791
|
-
if (input.witnessUtxo) {
|
|
792
|
-
return input.witnessUtxo.script;
|
|
793
|
-
}
|
|
794
|
-
if (input.nonWitnessUtxo) {
|
|
795
|
-
// Additional logic can be added to extract script from nonWitnessUtxo
|
|
796
|
-
return null;
|
|
797
|
-
}
|
|
798
|
-
return null;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// Helper method to check if a public key is in a script
|
|
802
|
-
private pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
|
|
803
|
-
return this.pubkeyPositionInScript(pubkey, script) !== -1;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
private pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
|
|
807
|
-
const pubkeyHash = bitCrypto.hash160(pubkey);
|
|
808
|
-
const pubkeyXOnly = toXOnly(pubkey);
|
|
809
|
-
|
|
810
|
-
const decompiled = bscript.decompile(script);
|
|
811
|
-
if (decompiled === null) throw new Error('Unknown script error');
|
|
812
|
-
|
|
813
|
-
return decompiled.findIndex((element) => {
|
|
814
|
-
if (typeof element === 'number') return false;
|
|
815
|
-
return (
|
|
816
|
-
element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly)
|
|
817
|
-
);
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
|
|
821
884
|
private async signTaprootInput(
|
|
822
885
|
signer: Signer | ECPairInterface,
|
|
823
886
|
transaction: Psbt,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Utility class for Bitcoin related functions
|
|
@@ -34,16 +34,16 @@ export class BitcoinUtils {
|
|
|
34
34
|
window.crypto.getRandomValues(array);
|
|
35
35
|
|
|
36
36
|
return Buffer.from(array);
|
|
37
|
-
} else if (crypto && typeof crypto.getRandomValues === 'function') {
|
|
37
|
+
} else if (globalThis.crypto && typeof globalThis.crypto.getRandomValues === 'function') {
|
|
38
38
|
const array = new Uint8Array(length);
|
|
39
|
-
crypto.getRandomValues(array);
|
|
39
|
+
globalThis.crypto.getRandomValues(array);
|
|
40
40
|
|
|
41
41
|
return Buffer.from(array);
|
|
42
42
|
} else {
|
|
43
43
|
console.log(
|
|
44
44
|
`No secure random number generator available. Please upgrade your environment.`,
|
|
45
45
|
globalThis.window.crypto,
|
|
46
|
-
crypto,
|
|
46
|
+
globalThis.crypto,
|
|
47
47
|
);
|
|
48
48
|
throw new Error(
|
|
49
49
|
'No secure random number generator available. Please upgrade your environment.',
|
|
@@ -21,7 +21,7 @@ export class BufferHelper {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
public static uint8ArrayToHex(input: Uint8Array): string {
|
|
24
|
-
return Buffer.from(input, 0, input.byteLength).toString('hex');
|
|
24
|
+
return Buffer.from(input.buffer, 0, input.byteLength).toString('hex');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
public static hexToUint8Array(input: string): Uint8Array {
|
|
@@ -5,6 +5,7 @@ export interface UTXO {
|
|
|
5
5
|
readonly outputIndex: number;
|
|
6
6
|
readonly value: bigint;
|
|
7
7
|
readonly scriptPubKey: ScriptPubKey;
|
|
8
|
+
|
|
8
9
|
redeemScript?: string | Buffer;
|
|
9
10
|
witnessScript?: string | Buffer;
|
|
10
11
|
nonWitnessUtxo?: string | Buffer;
|
|
@@ -31,4 +32,5 @@ export interface RawUTXOResponse {
|
|
|
31
32
|
readonly outputIndex: number;
|
|
32
33
|
readonly value: string;
|
|
33
34
|
readonly scriptPubKey: ScriptPubKey;
|
|
35
|
+
readonly raw: string;
|
|
34
36
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
crypto as bitCrypto,
|
|
3
|
+
Network,
|
|
4
|
+
networks,
|
|
5
|
+
Payment,
|
|
6
|
+
payments,
|
|
7
|
+
Taptree,
|
|
8
|
+
toXOnly,
|
|
9
|
+
} from '@btc-vision/bitcoin';
|
|
4
10
|
import { DeploymentGenerator } from '../generators/builders/DeploymentGenerator.js';
|
|
5
11
|
import { TransactionBuilder } from '../transaction/builders/TransactionBuilder.js';
|
|
6
12
|
import { Address } from '../keypair/Address.js';
|