@btc-vision/bitcoin 7.0.0-alpha.0 → 7.0.0-alpha.2
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/address.d.ts +6 -2
- package/browser/address.d.ts.map +1 -1
- package/browser/block.d.ts.map +1 -1
- package/browser/branded.d.ts +3 -14
- package/browser/branded.d.ts.map +1 -1
- package/browser/crypto.d.ts +1 -1
- package/browser/ecc/context.d.ts +4 -4
- package/browser/ecc/context.d.ts.map +1 -1
- package/browser/ecc/types.d.ts +1 -1
- package/browser/ecc/types.d.ts.map +1 -1
- package/browser/index.d.ts +3 -2
- package/browser/index.d.ts.map +1 -1
- package/browser/index.js +3579 -3539
- package/browser/io/BinaryReader.d.ts +15 -15
- package/browser/io/BinaryReader.d.ts.map +1 -1
- package/browser/io/BinaryWriter.d.ts +17 -17
- package/browser/io/BinaryWriter.d.ts.map +1 -1
- package/browser/io/MemoryPool.d.ts +20 -20
- package/browser/io/MemoryPool.d.ts.map +1 -1
- package/browser/opcodes.d.ts +11 -0
- package/browser/opcodes.d.ts.map +1 -1
- package/browser/payments/bip341.d.ts +1 -1
- package/browser/payments/bip341.d.ts.map +1 -1
- package/browser/payments/embed.d.ts +1 -1
- package/browser/payments/embed.d.ts.map +1 -1
- package/browser/payments/p2ms.d.ts.map +1 -1
- package/browser/payments/p2op.d.ts +1 -1
- package/browser/payments/p2op.d.ts.map +1 -1
- package/browser/payments/p2pk.d.ts +1 -1
- package/browser/payments/p2pk.d.ts.map +1 -1
- package/browser/payments/p2pkh.d.ts +1 -1
- package/browser/payments/p2pkh.d.ts.map +1 -1
- package/browser/payments/p2sh.d.ts.map +1 -1
- package/browser/payments/p2tr.d.ts +2 -2
- package/browser/payments/p2tr.d.ts.map +1 -1
- package/browser/payments/p2wpkh.d.ts +1 -1
- package/browser/payments/p2wpkh.d.ts.map +1 -1
- package/browser/payments/p2wsh.d.ts.map +1 -1
- package/browser/payments/types.d.ts +1 -1
- package/browser/payments/types.d.ts.map +1 -1
- package/browser/psbt/PsbtCache.d.ts +54 -0
- package/browser/psbt/PsbtCache.d.ts.map +1 -0
- package/browser/psbt/PsbtFinalizer.d.ts +21 -0
- package/browser/psbt/PsbtFinalizer.d.ts.map +1 -0
- package/browser/psbt/PsbtSigner.d.ts +32 -0
- package/browser/psbt/PsbtSigner.d.ts.map +1 -0
- package/browser/psbt/PsbtTransaction.d.ts +25 -0
- package/browser/psbt/PsbtTransaction.d.ts.map +1 -0
- package/browser/psbt/bip371.d.ts.map +1 -1
- package/browser/psbt/types.d.ts +14 -14
- package/browser/psbt/types.d.ts.map +1 -1
- package/browser/psbt/validation.d.ts +1 -1
- package/browser/psbt/validation.d.ts.map +1 -1
- package/browser/psbt.d.ts +27 -39
- package/browser/psbt.d.ts.map +1 -1
- package/browser/script.d.ts.map +1 -1
- package/browser/transaction.d.ts +4 -4
- package/browser/transaction.d.ts.map +1 -1
- package/browser/types.d.ts +4 -2
- package/browser/types.d.ts.map +1 -1
- package/browser/workers/WorkerSigningPool.d.ts +17 -17
- package/browser/workers/WorkerSigningPool.d.ts.map +1 -1
- package/browser/workers/WorkerSigningPool.node.d.ts +12 -12
- package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -1
- package/browser/workers/index.d.ts +3 -50
- package/browser/workers/index.d.ts.map +1 -1
- package/browser/workers/index.node.d.ts +24 -0
- package/browser/workers/index.node.d.ts.map +1 -0
- package/browser/workers/psbt-parallel.d.ts +1 -1
- package/browser/workers/psbt-parallel.d.ts.map +1 -1
- package/browser/workers/types.d.ts.map +1 -1
- package/build/address.d.ts +6 -2
- package/build/address.d.ts.map +1 -1
- package/build/address.js +32 -19
- package/build/address.js.map +1 -1
- package/build/block.d.ts.map +1 -1
- package/build/block.js +2 -4
- package/build/block.js.map +1 -1
- package/build/branded.d.ts +3 -14
- package/build/branded.d.ts.map +1 -1
- package/build/branded.js +0 -5
- package/build/branded.js.map +1 -1
- package/build/crypto.d.ts +1 -1
- package/build/ecc/context.d.ts +4 -4
- package/build/ecc/context.d.ts.map +1 -1
- package/build/ecc/context.js +75 -52
- package/build/ecc/context.js.map +1 -1
- package/build/ecc/types.d.ts +1 -1
- package/build/ecc/types.d.ts.map +1 -1
- package/build/index.d.ts +3 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/io/BinaryReader.d.ts +15 -15
- package/build/io/BinaryReader.d.ts.map +1 -1
- package/build/io/BinaryReader.js +17 -17
- package/build/io/BinaryReader.js.map +1 -1
- package/build/io/BinaryWriter.d.ts +17 -17
- package/build/io/BinaryWriter.d.ts.map +1 -1
- package/build/io/BinaryWriter.js +39 -39
- package/build/io/BinaryWriter.js.map +1 -1
- package/build/io/MemoryPool.d.ts +20 -20
- package/build/io/MemoryPool.d.ts.map +1 -1
- package/build/io/MemoryPool.js +28 -28
- package/build/io/MemoryPool.js.map +1 -1
- package/build/opcodes.d.ts +11 -0
- package/build/opcodes.d.ts.map +1 -1
- package/build/opcodes.js +19 -4
- package/build/opcodes.js.map +1 -1
- package/build/payments/bip341.d.ts +1 -2
- package/build/payments/bip341.d.ts.map +1 -1
- package/build/payments/bip341.js +1 -2
- package/build/payments/bip341.js.map +1 -1
- package/build/payments/embed.d.ts +1 -1
- package/build/payments/embed.d.ts.map +1 -1
- package/build/payments/embed.js +14 -14
- package/build/payments/embed.js.map +1 -1
- package/build/payments/p2ms.d.ts.map +1 -1
- package/build/payments/p2ms.js +21 -21
- package/build/payments/p2ms.js.map +1 -1
- package/build/payments/p2op.d.ts +1 -1
- package/build/payments/p2op.d.ts.map +1 -1
- package/build/payments/p2op.js +18 -18
- package/build/payments/p2op.js.map +1 -1
- package/build/payments/p2pk.d.ts +1 -1
- package/build/payments/p2pk.d.ts.map +1 -1
- package/build/payments/p2pk.js +17 -17
- package/build/payments/p2pk.js.map +1 -1
- package/build/payments/p2pkh.d.ts +1 -1
- package/build/payments/p2pkh.d.ts.map +1 -1
- package/build/payments/p2pkh.js +20 -20
- package/build/payments/p2pkh.js.map +1 -1
- package/build/payments/p2sh.d.ts.map +1 -1
- package/build/payments/p2sh.js +22 -20
- package/build/payments/p2sh.js.map +1 -1
- package/build/payments/p2tr.d.ts +2 -2
- package/build/payments/p2tr.d.ts.map +1 -1
- package/build/payments/p2tr.js +23 -23
- package/build/payments/p2tr.js.map +1 -1
- package/build/payments/p2wpkh.d.ts +1 -1
- package/build/payments/p2wpkh.d.ts.map +1 -1
- package/build/payments/p2wpkh.js +20 -20
- package/build/payments/p2wpkh.js.map +1 -1
- package/build/payments/p2wsh.d.ts.map +1 -1
- package/build/payments/p2wsh.js +22 -22
- package/build/payments/p2wsh.js.map +1 -1
- package/build/payments/types.d.ts +1 -1
- package/build/payments/types.d.ts.map +1 -1
- package/build/psbt/PsbtCache.d.ts +54 -0
- package/build/psbt/PsbtCache.d.ts.map +1 -0
- package/build/psbt/PsbtCache.js +249 -0
- package/build/psbt/PsbtCache.js.map +1 -0
- package/build/psbt/PsbtFinalizer.d.ts +21 -0
- package/build/psbt/PsbtFinalizer.d.ts.map +1 -0
- package/build/psbt/PsbtFinalizer.js +157 -0
- package/build/psbt/PsbtFinalizer.js.map +1 -0
- package/build/psbt/PsbtSigner.d.ts +32 -0
- package/build/psbt/PsbtSigner.d.ts.map +1 -0
- package/build/psbt/PsbtSigner.js +192 -0
- package/build/psbt/PsbtSigner.js.map +1 -0
- package/build/psbt/PsbtTransaction.d.ts +25 -0
- package/build/psbt/PsbtTransaction.d.ts.map +1 -0
- package/build/psbt/PsbtTransaction.js +61 -0
- package/build/psbt/PsbtTransaction.js.map +1 -0
- package/build/psbt/bip371.d.ts.map +1 -1
- package/build/psbt/bip371.js +6 -2
- package/build/psbt/bip371.js.map +1 -1
- package/build/psbt/psbtutils.js +1 -1
- package/build/psbt/psbtutils.js.map +1 -1
- package/build/psbt/types.d.ts +14 -14
- package/build/psbt/types.d.ts.map +1 -1
- package/build/psbt/validation.d.ts +1 -1
- package/build/psbt/validation.d.ts.map +1 -1
- package/build/psbt/validation.js +1 -1
- package/build/psbt/validation.js.map +1 -1
- package/build/psbt.d.ts +27 -39
- package/build/psbt.d.ts.map +1 -1
- package/build/psbt.js +142 -755
- package/build/psbt.js.map +1 -1
- package/build/script.d.ts.map +1 -1
- package/build/script.js +4 -4
- package/build/script.js.map +1 -1
- package/build/transaction.d.ts +4 -4
- package/build/transaction.d.ts.map +1 -1
- package/build/transaction.js +6 -5
- package/build/transaction.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/build/types.d.ts +4 -2
- package/build/types.d.ts.map +1 -1
- package/build/types.js +12 -9
- package/build/types.js.map +1 -1
- package/build/workers/WorkerSigningPool.d.ts +17 -17
- package/build/workers/WorkerSigningPool.d.ts.map +1 -1
- package/build/workers/WorkerSigningPool.js +25 -25
- package/build/workers/WorkerSigningPool.js.map +1 -1
- package/build/workers/WorkerSigningPool.node.d.ts +12 -12
- package/build/workers/WorkerSigningPool.node.d.ts.map +1 -1
- package/build/workers/WorkerSigningPool.node.js +23 -23
- package/build/workers/WorkerSigningPool.node.js.map +1 -1
- package/build/workers/index.d.ts +3 -3
- package/build/workers/index.d.ts.map +1 -1
- package/build/workers/index.js +0 -3
- package/build/workers/index.js.map +1 -1
- package/build/workers/index.node.d.ts +24 -0
- package/build/workers/index.node.d.ts.map +1 -0
- package/build/workers/index.node.js +26 -0
- package/build/workers/index.node.js.map +1 -0
- package/build/workers/psbt-parallel.d.ts +1 -1
- package/build/workers/psbt-parallel.d.ts.map +1 -1
- package/build/workers/psbt-parallel.js.map +1 -1
- package/build/workers/types.d.ts.map +1 -1
- package/build/workers/types.js.map +1 -1
- package/package.json +30 -10
- package/src/address.ts +53 -21
- package/src/block.ts +15 -8
- package/src/branded.ts +15 -13
- package/src/crypto.ts +1 -1
- package/src/ecc/context.ts +85 -64
- package/src/ecc/types.ts +1 -8
- package/src/index.ts +48 -14
- package/src/io/BinaryReader.ts +18 -18
- package/src/io/BinaryWriter.ts +43 -43
- package/src/io/MemoryPool.ts +32 -32
- package/src/opcodes.ts +21 -4
- package/src/payments/bip341.ts +2 -4
- package/src/payments/embed.ts +18 -18
- package/src/payments/p2ms.ts +32 -25
- package/src/payments/p2op.ts +22 -22
- package/src/payments/p2pk.ts +20 -20
- package/src/payments/p2pkh.ts +25 -25
- package/src/payments/p2sh.ts +30 -27
- package/src/payments/p2tr.ts +31 -31
- package/src/payments/p2wpkh.ts +25 -25
- package/src/payments/p2wsh.ts +27 -27
- package/src/payments/types.ts +1 -1
- package/src/psbt/PsbtCache.ts +325 -0
- package/src/psbt/PsbtFinalizer.ts +213 -0
- package/src/psbt/PsbtSigner.ts +302 -0
- package/src/psbt/PsbtTransaction.ts +82 -0
- package/src/psbt/bip371.ts +7 -3
- package/src/psbt/psbtutils.ts +1 -1
- package/src/psbt/types.ts +14 -21
- package/src/psbt/validation.ts +5 -12
- package/src/psbt.ts +363 -1130
- package/src/script.ts +6 -9
- package/src/transaction.ts +18 -14
- package/src/types.ts +28 -17
- package/src/workers/WorkerSigningPool.node.ts +31 -31
- package/src/workers/WorkerSigningPool.ts +35 -39
- package/src/workers/index.node.ts +27 -0
- package/src/workers/index.ts +7 -9
- package/src/workers/psbt-parallel.ts +2 -7
- package/src/workers/types.ts +5 -1
- package/test/address.spec.ts +2 -2
- package/test/bitcoin.core.spec.ts +5 -2
- package/test/browser/payments.spec.ts +151 -0
- package/test/browser/psbt.spec.ts +1510 -0
- package/test/browser/script.spec.ts +223 -0
- package/test/browser/setup.ts +13 -0
- package/test/browser/workers-signing.spec.ts +537 -0
- package/test/crypto.spec.ts +2 -2
- package/test/fixtures/core/base58_encode_decode.json +12 -48
- package/test/fixtures/core/base58_keys_invalid.json +50 -150
- package/test/fixtures/core/sighash.json +1 -3
- package/test/fixtures/core/tx_valid.json +133 -501
- package/test/fixtures/embed.json +3 -11
- package/test/fixtures/p2ms.json +21 -91
- package/test/fixtures/p2pk.json +5 -24
- package/test/fixtures/p2pkh.json +7 -36
- package/test/fixtures/p2sh.json +8 -54
- package/test/fixtures/p2tr.json +2 -6
- package/test/fixtures/p2wpkh.json +7 -36
- package/test/fixtures/p2wsh.json +14 -59
- package/test/fixtures/psbt.json +2 -6
- package/test/fixtures/script.json +12 -48
- package/test/integration/addresses.spec.ts +11 -5
- package/test/integration/bip32.spec.ts +1 -1
- package/test/integration/cltv.spec.ts +10 -6
- package/test/integration/csv.spec.ts +10 -9
- package/test/integration/payments.spec.ts +8 -4
- package/test/integration/taproot.spec.ts +26 -6
- package/test/integration/transactions.spec.ts +22 -8
- package/test/payments.spec.ts +1 -1
- package/test/payments.utils.ts +1 -1
- package/test/psbt.spec.ts +250 -64
- package/test/script_signature.spec.ts +1 -1
- package/test/transaction.spec.ts +18 -5
- package/test/tsconfig.json +6 -20
- package/test/workers-pool.spec.ts +22 -23
- package/test/workers-signing.spec.ts +7 -3
- package/test/workers.spec.ts +6 -7
- package/typedoc.json +39 -0
- package/vitest.config.browser.ts +68 -0
- package/browser/ecpair.d.ts +0 -99
- package/src/ecpair.d.ts +0 -99
- package/test/taproot-cache.spec.ts +0 -694
package/src/psbt.ts
CHANGED
|
@@ -1,32 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Psbt as PsbtBase,
|
|
3
|
-
checkForInput,
|
|
4
|
-
checkForOutput,
|
|
5
|
-
} from 'bip174';
|
|
6
1
|
import type {
|
|
7
|
-
Bip32Derivation,
|
|
8
2
|
KeyValue,
|
|
9
|
-
PartialSig,
|
|
10
3
|
PsbtGlobalUpdate,
|
|
11
4
|
PsbtInput,
|
|
12
5
|
PsbtInputUpdate,
|
|
13
|
-
PsbtOutput,
|
|
14
6
|
PsbtOutputUpdate,
|
|
15
7
|
TapKeySig,
|
|
16
8
|
TapScriptSig,
|
|
17
|
-
Transaction as ITransaction,
|
|
18
|
-
TransactionFromBuffer,
|
|
19
9
|
} from 'bip174';
|
|
20
|
-
import {
|
|
10
|
+
import { checkForInput, checkForOutput, Psbt as PsbtBase } from 'bip174';
|
|
11
|
+
import { clone, equals, fromBase64, fromHex, toHex } from './io/index.js';
|
|
21
12
|
|
|
22
13
|
import type { BIP32Interface } from '@btc-vision/bip32';
|
|
23
|
-
import
|
|
24
|
-
import { fromOutputScript, isUnknownSegwitVersion, toOutputScript } from './address.js';
|
|
14
|
+
import { fromOutputScript, toOutputScript } from './address.js';
|
|
25
15
|
import { bitcoin as btcNetwork } from './networks.js';
|
|
26
16
|
import * as payments from './payments/index.js';
|
|
27
|
-
import type { P2WSHPayment } from './payments/index.js';
|
|
28
|
-
import { tapleafHash } from './payments/bip341.js';
|
|
29
|
-
import type { P2SHPayment } from './payments/index.js';
|
|
30
17
|
import {
|
|
31
18
|
checkTaprootInputFields,
|
|
32
19
|
checkTaprootOutputFields,
|
|
@@ -35,38 +22,52 @@ import {
|
|
|
35
22
|
tapScriptFinalizer,
|
|
36
23
|
} from './psbt/bip371.js';
|
|
37
24
|
import { toXOnly } from './pubkey.js';
|
|
25
|
+
import * as bscript from './script.js';
|
|
26
|
+
import { Transaction } from './transaction.js';
|
|
27
|
+
import type { Bytes32, MessageHash, PublicKey, SchnorrSignature, Script } from './types.js';
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
AllScriptType,
|
|
31
|
+
FinalScriptsFunc,
|
|
32
|
+
FinalTaprootScriptsFunc,
|
|
33
|
+
HDSigner,
|
|
34
|
+
HDSignerAsync,
|
|
35
|
+
PsbtBaseExtended,
|
|
36
|
+
PsbtInputExtended,
|
|
37
|
+
PsbtOpts,
|
|
38
|
+
PsbtOptsOptional,
|
|
39
|
+
PsbtOutputExtended,
|
|
40
|
+
PsbtOutputExtendedAddress,
|
|
41
|
+
PsbtTxInput,
|
|
42
|
+
PsbtTxOutput,
|
|
43
|
+
Signer,
|
|
44
|
+
SignerAlternative,
|
|
45
|
+
SignerAsync,
|
|
46
|
+
TaprootHashCheckSigner,
|
|
47
|
+
ValidateSigFunction,
|
|
48
|
+
} from './psbt/types.js';
|
|
49
|
+
// Import composition classes
|
|
50
|
+
import { PsbtCache } from './psbt/PsbtCache.js';
|
|
51
|
+
import { PsbtSigner } from './psbt/PsbtSigner.js';
|
|
38
52
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} from './psbt/
|
|
53
|
+
getFinalScripts as _getFinalScripts,
|
|
54
|
+
prepareFinalScripts as _prepareFinalScripts,
|
|
55
|
+
PsbtFinalizer,
|
|
56
|
+
} from './psbt/PsbtFinalizer.js';
|
|
57
|
+
import { PsbtTransaction, transactionFromBuffer } from './psbt/PsbtTransaction.js';
|
|
44
58
|
import {
|
|
45
59
|
check32Bit,
|
|
46
60
|
checkCache,
|
|
47
61
|
checkInputsForPartialSig,
|
|
48
62
|
checkPartialSigSighashes,
|
|
49
63
|
checkScriptForPubkey,
|
|
50
|
-
checkTxEmpty,
|
|
51
64
|
checkTxForDupeIns,
|
|
52
65
|
checkTxInputCache,
|
|
53
66
|
isFinalized,
|
|
54
67
|
} from './psbt/validation.js';
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
compressPubkey,
|
|
59
|
-
getMeaningfulScript,
|
|
60
|
-
isPubkeyLike,
|
|
61
|
-
isSigLike,
|
|
62
|
-
range,
|
|
63
|
-
scriptWitnessToWitnessStack,
|
|
64
|
-
sighashTypeToString,
|
|
65
|
-
} from './psbt/utils.js';
|
|
66
|
-
import * as bscript from './script.js';
|
|
67
|
-
import { Transaction } from './transaction.js';
|
|
68
|
-
import type { Output } from './transaction.js';
|
|
69
|
-
import type { Bytes20, Bytes32, PublicKey, Satoshi, SchnorrSignature, Signature, Script, XOnlyPublicKey } from './types.js';
|
|
68
|
+
import { checkInvalidP2WSH, classifyScript, getMeaningfulScript, range } from './psbt/utils.js';
|
|
69
|
+
import { witnessStackToScriptWitness } from './psbt/psbtutils.js';
|
|
70
|
+
import type { UniversalSigner } from '@btc-vision/ecpair';
|
|
70
71
|
|
|
71
72
|
// Re-export types from the types module
|
|
72
73
|
export type {
|
|
@@ -80,7 +81,6 @@ export type {
|
|
|
80
81
|
PsbtOpts,
|
|
81
82
|
PsbtInputExtended,
|
|
82
83
|
PsbtOutputExtended,
|
|
83
|
-
PsbtOutputExtendedAddress,
|
|
84
84
|
PsbtOutputExtendedScript,
|
|
85
85
|
HDSigner,
|
|
86
86
|
HDSignerAsync,
|
|
@@ -88,7 +88,7 @@ export type {
|
|
|
88
88
|
Signer,
|
|
89
89
|
SignerAsync,
|
|
90
90
|
TaprootHashCheckSigner,
|
|
91
|
-
|
|
91
|
+
PsbtCacheInterface,
|
|
92
92
|
TxCacheNumberKey,
|
|
93
93
|
ScriptType,
|
|
94
94
|
AllScriptType,
|
|
@@ -97,50 +97,75 @@ export type {
|
|
|
97
97
|
FinalTaprootScriptsFunc,
|
|
98
98
|
} from './psbt/types.js';
|
|
99
99
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
PsbtBaseExtended,
|
|
107
|
-
PsbtOptsOptional,
|
|
108
|
-
PsbtOpts,
|
|
109
|
-
PsbtInputExtended,
|
|
110
|
-
PsbtOutputExtended,
|
|
111
|
-
PsbtOutputExtendedAddress,
|
|
112
|
-
HDSigner,
|
|
113
|
-
HDSignerAsync,
|
|
114
|
-
SignerAlternative,
|
|
115
|
-
Signer,
|
|
116
|
-
SignerAsync,
|
|
117
|
-
TaprootHashCheckSigner,
|
|
118
|
-
PsbtCache,
|
|
119
|
-
TxCacheNumberKey,
|
|
120
|
-
AllScriptType,
|
|
121
|
-
GetScriptReturn,
|
|
122
|
-
FinalScriptsFunc,
|
|
123
|
-
FinalTaprootScriptsFunc,
|
|
124
|
-
ValidateSigFunction,
|
|
125
|
-
} from './psbt/types.js';
|
|
100
|
+
// Re-export for backwards compatibility
|
|
101
|
+
export { getFinalScripts, prepareFinalScripts };
|
|
102
|
+
export { PsbtCache } from './psbt/PsbtCache.js';
|
|
103
|
+
export { PsbtSigner } from './psbt/PsbtSigner.js';
|
|
104
|
+
export { PsbtFinalizer } from './psbt/PsbtFinalizer.js';
|
|
105
|
+
export { PsbtTransaction, transactionFromBuffer } from './psbt/PsbtTransaction.js';
|
|
126
106
|
|
|
127
107
|
/**
|
|
128
108
|
* These are the default arguments for a Psbt instance.
|
|
129
109
|
*/
|
|
130
110
|
const DEFAULT_OPTS: PsbtOpts = {
|
|
131
|
-
/**
|
|
132
|
-
* A bitcoinjs Network object. This is only used if you pass an `address`
|
|
133
|
-
* parameter to addOutput. Otherwise it is not needed and can be left default.
|
|
134
|
-
*/
|
|
135
111
|
network: btcNetwork,
|
|
136
|
-
|
|
137
|
-
* When extractTransaction is called, the fee rate is checked.
|
|
138
|
-
* THIS IS NOT TO BE RELIED ON.
|
|
139
|
-
* It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
|
|
140
|
-
*/
|
|
141
|
-
maximumFeeRate: 5000, // satoshi per byte
|
|
112
|
+
maximumFeeRate: 5000,
|
|
142
113
|
};
|
|
143
114
|
|
|
115
|
+
/** Helper to create a Transaction from a buffer */
|
|
116
|
+
function txFromBuffer(buf: Uint8Array): Transaction {
|
|
117
|
+
return Transaction.fromBuffer(buf);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Standalone exports that delegate to PsbtFinalizer
|
|
121
|
+
function getFinalScripts(
|
|
122
|
+
inputIndex: number,
|
|
123
|
+
input: PsbtInput,
|
|
124
|
+
script: Script,
|
|
125
|
+
isSegwit: boolean,
|
|
126
|
+
isP2SH: boolean,
|
|
127
|
+
isP2WSH: boolean,
|
|
128
|
+
canRunChecks: boolean = true,
|
|
129
|
+
solution?: Uint8Array[],
|
|
130
|
+
): {
|
|
131
|
+
finalScriptSig: Script | undefined;
|
|
132
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
133
|
+
} {
|
|
134
|
+
return _getFinalScripts(
|
|
135
|
+
inputIndex,
|
|
136
|
+
input,
|
|
137
|
+
script,
|
|
138
|
+
isSegwit,
|
|
139
|
+
isP2SH,
|
|
140
|
+
isP2WSH,
|
|
141
|
+
canRunChecks,
|
|
142
|
+
solution,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function prepareFinalScripts(
|
|
147
|
+
script: Uint8Array,
|
|
148
|
+
scriptType: string,
|
|
149
|
+
partialSig: import('bip174').PartialSig[],
|
|
150
|
+
isSegwit: boolean,
|
|
151
|
+
isP2SH: boolean,
|
|
152
|
+
isP2WSH: boolean,
|
|
153
|
+
solution?: Uint8Array[],
|
|
154
|
+
): {
|
|
155
|
+
finalScriptSig: Script | undefined;
|
|
156
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
157
|
+
} {
|
|
158
|
+
return _prepareFinalScripts(
|
|
159
|
+
script,
|
|
160
|
+
scriptType,
|
|
161
|
+
partialSig,
|
|
162
|
+
isSegwit,
|
|
163
|
+
isP2SH,
|
|
164
|
+
isP2WSH,
|
|
165
|
+
solution,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
144
169
|
/**
|
|
145
170
|
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
146
171
|
* There are 6 roles that this class fulfills. (Explained in BIP174)
|
|
@@ -178,64 +203,46 @@ const DEFAULT_OPTS: PsbtOpts = {
|
|
|
178
203
|
* Transaction Extractor: This role will perform some checks before returning a
|
|
179
204
|
* Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
|
|
180
205
|
*/
|
|
181
|
-
/**
|
|
182
|
-
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
183
|
-
*/
|
|
184
206
|
export class Psbt {
|
|
185
207
|
readonly #cache: PsbtCache;
|
|
208
|
+
#signer: PsbtSigner | undefined;
|
|
209
|
+
#finalizer: PsbtFinalizer | undefined;
|
|
186
210
|
readonly #opts: PsbtOpts;
|
|
187
211
|
|
|
188
|
-
constructor(
|
|
212
|
+
public constructor(
|
|
189
213
|
opts: PsbtOptsOptional = {},
|
|
190
214
|
public data: PsbtBaseExtended = new PsbtBase(new PsbtTransaction()),
|
|
191
215
|
) {
|
|
192
216
|
this.#opts = Object.assign({}, DEFAULT_OPTS, opts);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
nonWitnessUtxoBufCache: [],
|
|
196
|
-
txInCache: {},
|
|
197
|
-
// unsignedTx.tx property is dynamically added by PsbtBase
|
|
198
|
-
tx: (this.data.globalMap.unsignedTx as PsbtTransaction).tx,
|
|
199
|
-
unsafeSignNonSegwit: false,
|
|
200
|
-
hasSignatures: false,
|
|
201
|
-
};
|
|
217
|
+
const tx = (this.data.globalMap.unsignedTx as PsbtTransaction).tx;
|
|
218
|
+
this.#cache = new PsbtCache(tx);
|
|
202
219
|
|
|
203
220
|
if (opts.version === 3) {
|
|
204
221
|
this.setVersionTRUC();
|
|
205
222
|
} else if (this.data.inputs.length === 0) this.setVersion(2);
|
|
206
223
|
}
|
|
207
224
|
|
|
208
|
-
|
|
209
|
-
get __CACHE(): PsbtCache {
|
|
210
|
-
return this.#cache;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** @internal - Exposed for testing. Do not use in production code. */
|
|
214
|
-
get opts(): PsbtOpts {
|
|
215
|
-
return this.#opts;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
get inputCount(): number {
|
|
225
|
+
public get inputCount(): number {
|
|
219
226
|
return this.data.inputs.length;
|
|
220
227
|
}
|
|
221
228
|
|
|
222
|
-
get version(): number {
|
|
229
|
+
public get version(): number {
|
|
223
230
|
return this.#cache.tx.version;
|
|
224
231
|
}
|
|
225
232
|
|
|
226
|
-
set version(version: number) {
|
|
233
|
+
public set version(version: number) {
|
|
227
234
|
this.setVersion(version);
|
|
228
235
|
}
|
|
229
236
|
|
|
230
|
-
get locktime(): number {
|
|
237
|
+
public get locktime(): number {
|
|
231
238
|
return this.#cache.tx.locktime;
|
|
232
239
|
}
|
|
233
240
|
|
|
234
|
-
set locktime(locktime: number) {
|
|
241
|
+
public set locktime(locktime: number) {
|
|
235
242
|
this.setLocktime(locktime);
|
|
236
243
|
}
|
|
237
244
|
|
|
238
|
-
get txInputs(): PsbtTxInput[] {
|
|
245
|
+
public get txInputs(): PsbtTxInput[] {
|
|
239
246
|
return this.#cache.tx.ins.map((input) => ({
|
|
240
247
|
hash: clone(input.hash) as Bytes32,
|
|
241
248
|
index: input.index,
|
|
@@ -243,12 +250,14 @@ export class Psbt {
|
|
|
243
250
|
}));
|
|
244
251
|
}
|
|
245
252
|
|
|
246
|
-
get txOutputs(): PsbtTxOutput[] {
|
|
253
|
+
public get txOutputs(): PsbtTxOutput[] {
|
|
247
254
|
return this.#cache.tx.outs.map((output) => {
|
|
248
|
-
let address;
|
|
255
|
+
let address: string | undefined;
|
|
249
256
|
try {
|
|
250
257
|
address = fromOutputScript(output.script, this.#opts.network);
|
|
251
|
-
} catch (_) {
|
|
258
|
+
} catch (_) {
|
|
259
|
+
// Not all scripts can be converted to an address
|
|
260
|
+
}
|
|
252
261
|
return {
|
|
253
262
|
script: clone(output.script) as Script,
|
|
254
263
|
value: output.value,
|
|
@@ -257,21 +266,36 @@ export class Psbt {
|
|
|
257
266
|
});
|
|
258
267
|
}
|
|
259
268
|
|
|
260
|
-
|
|
269
|
+
/** Lazily initialized signer - created on first access */
|
|
270
|
+
get #lazySigner(): PsbtSigner {
|
|
271
|
+
if (!this.#signer) {
|
|
272
|
+
this.#signer = new PsbtSigner(this.#cache, txFromBuffer);
|
|
273
|
+
}
|
|
274
|
+
return this.#signer;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Lazily initialized finalizer - created on first access */
|
|
278
|
+
get #lazyFinalizer(): PsbtFinalizer {
|
|
279
|
+
if (!this.#finalizer) {
|
|
280
|
+
this.#finalizer = new PsbtFinalizer(this.#cache, txFromBuffer);
|
|
281
|
+
}
|
|
282
|
+
return this.#finalizer;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
261
286
|
const buffer = fromBase64(data);
|
|
262
287
|
return this.fromBuffer(buffer, opts);
|
|
263
288
|
}
|
|
264
289
|
|
|
265
|
-
static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
290
|
+
public static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
266
291
|
const buffer = fromHex(data);
|
|
267
292
|
return this.fromBuffer(buffer, opts);
|
|
268
293
|
}
|
|
269
294
|
|
|
270
|
-
static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
|
|
295
|
+
public static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
|
|
271
296
|
const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
|
|
272
297
|
const psbt = new Psbt(opts, psbtBase);
|
|
273
298
|
checkTxForDupeIns(psbt.#cache.tx, psbt.#cache);
|
|
274
|
-
// Check if restored PSBT has any signatures (partial or finalized)
|
|
275
299
|
psbt.#cache.hasSignatures = psbt.data.inputs.some(
|
|
276
300
|
(input) =>
|
|
277
301
|
input.partialSig?.length ||
|
|
@@ -283,62 +307,62 @@ export class Psbt {
|
|
|
283
307
|
return psbt;
|
|
284
308
|
}
|
|
285
309
|
|
|
286
|
-
combine(...those: Psbt[]): this {
|
|
310
|
+
public combine(...those: Psbt[]): this {
|
|
287
311
|
this.data.combine(...those.map((o) => o.data));
|
|
288
312
|
return this;
|
|
289
313
|
}
|
|
290
314
|
|
|
291
|
-
clone(): Psbt {
|
|
292
|
-
|
|
293
|
-
const clonedOpts = JSON.parse(JSON.stringify(this.#opts)) as PsbtOptsOptional;
|
|
315
|
+
public clone(): Psbt {
|
|
316
|
+
const clonedOpts = structuredClone(this.#opts) as PsbtOptsOptional;
|
|
294
317
|
return Psbt.fromBuffer(new Uint8Array(this.data.toBuffer()), clonedOpts);
|
|
295
318
|
}
|
|
296
319
|
|
|
297
|
-
|
|
298
|
-
|
|
320
|
+
public get maximumFeeRate(): number {
|
|
321
|
+
return this.#opts.maximumFeeRate;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public setMaximumFeeRate(satoshiPerByte: number): void {
|
|
325
|
+
check32Bit(satoshiPerByte);
|
|
299
326
|
this.#opts.maximumFeeRate = satoshiPerByte;
|
|
300
327
|
}
|
|
301
328
|
|
|
302
|
-
setVersion(version: number): this {
|
|
329
|
+
public setVersion(version: number): this {
|
|
303
330
|
check32Bit(version);
|
|
304
331
|
checkInputsForPartialSig(this.data.inputs, 'setVersion', this.#cache.hasSignatures);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
c.extractedTx = undefined;
|
|
332
|
+
this.#cache.tx.version = version;
|
|
333
|
+
this.#cache.invalidate('outputs');
|
|
308
334
|
return this;
|
|
309
335
|
}
|
|
310
336
|
|
|
311
|
-
setVersionTRUC(): this {
|
|
337
|
+
public setVersionTRUC(): this {
|
|
312
338
|
return this.setVersion(Transaction.TRUC_VERSION);
|
|
313
339
|
}
|
|
314
340
|
|
|
315
|
-
setLocktime(locktime: number): this {
|
|
341
|
+
public setLocktime(locktime: number): this {
|
|
316
342
|
check32Bit(locktime);
|
|
317
343
|
checkInputsForPartialSig(this.data.inputs, 'setLocktime', this.#cache.hasSignatures);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
c.extractedTx = undefined;
|
|
344
|
+
this.#cache.tx.locktime = locktime;
|
|
345
|
+
this.#cache.invalidate('outputs');
|
|
321
346
|
return this;
|
|
322
347
|
}
|
|
323
348
|
|
|
324
|
-
setInputSequence(inputIndex: number, sequence: number): this {
|
|
349
|
+
public setInputSequence(inputIndex: number, sequence: number): this {
|
|
325
350
|
check32Bit(sequence);
|
|
326
351
|
checkInputsForPartialSig(this.data.inputs, 'setInputSequence', this.#cache.hasSignatures);
|
|
327
|
-
|
|
328
|
-
if (c.tx.ins.length <= inputIndex) {
|
|
352
|
+
if (this.#cache.tx.ins.length <= inputIndex) {
|
|
329
353
|
throw new Error('Input index too high');
|
|
330
354
|
}
|
|
331
|
-
|
|
332
|
-
|
|
355
|
+
this.#cache.tx.ins[inputIndex]!.sequence = sequence;
|
|
356
|
+
this.#cache.invalidate('outputs');
|
|
333
357
|
return this;
|
|
334
358
|
}
|
|
335
359
|
|
|
336
|
-
addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs: boolean = true): this {
|
|
360
|
+
public addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs: boolean = true): this {
|
|
337
361
|
inputDatas.forEach((inputData) => this.addInput(inputData, checkPartialSigs));
|
|
338
362
|
return this;
|
|
339
363
|
}
|
|
340
364
|
|
|
341
|
-
addInput(inputData: PsbtInputExtended, checkPartialSigs: boolean = true): this {
|
|
365
|
+
public addInput(inputData: PsbtInputExtended, checkPartialSigs: boolean = true): this {
|
|
342
366
|
if (!inputData || inputData.hash === undefined || inputData.index === undefined) {
|
|
343
367
|
throw new Error(
|
|
344
368
|
`Invalid arguments for Psbt.addInput. ` +
|
|
@@ -353,7 +377,6 @@ export class Psbt {
|
|
|
353
377
|
}
|
|
354
378
|
|
|
355
379
|
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
|
|
356
|
-
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
357
380
|
const normalizedInputData = inputData.witnessUtxo
|
|
358
381
|
? {
|
|
359
382
|
...inputData,
|
|
@@ -366,50 +389,28 @@ export class Psbt {
|
|
|
366
389
|
},
|
|
367
390
|
}
|
|
368
391
|
: inputData;
|
|
369
|
-
const c = this.#cache;
|
|
370
392
|
this.data.addInput(normalizedInputData);
|
|
371
|
-
const txIn =
|
|
372
|
-
checkTxInputCache(
|
|
393
|
+
const txIn = this.#cache.tx.ins[this.#cache.tx.ins.length - 1]!;
|
|
394
|
+
checkTxInputCache(this.#cache, txIn);
|
|
373
395
|
|
|
374
396
|
const inputIndex = this.data.inputs.length - 1;
|
|
375
397
|
const input = this.data.inputs[inputIndex]!;
|
|
376
398
|
if (input.nonWitnessUtxo) {
|
|
377
|
-
|
|
399
|
+
this.#cache.addNonWitnessTxCache(input, inputIndex, txFromBuffer);
|
|
378
400
|
}
|
|
379
|
-
|
|
380
|
-
c.feeRate = undefined;
|
|
381
|
-
c.extractedTx = undefined;
|
|
382
|
-
c.prevOuts = undefined;
|
|
383
|
-
c.signingScripts = undefined;
|
|
384
|
-
c.values = undefined;
|
|
385
|
-
c.taprootHashCache = undefined;
|
|
401
|
+
this.#cache.invalidate('full');
|
|
386
402
|
return this;
|
|
387
403
|
}
|
|
388
404
|
|
|
389
|
-
addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs: boolean = true): this {
|
|
405
|
+
public addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs: boolean = true): this {
|
|
390
406
|
outputDatas.forEach((outputData) => this.addOutput(outputData, checkPartialSigs));
|
|
391
407
|
return this;
|
|
392
408
|
}
|
|
393
409
|
|
|
394
|
-
|
|
395
|
-
* Add an output to the PSBT.
|
|
396
|
-
*
|
|
397
|
-
* **PERFORMANCE WARNING:** Passing an `address` string is ~10x slower than passing
|
|
398
|
-
* a `script` directly due to address parsing overhead (bech32 decode, etc.).
|
|
399
|
-
* For high-performance use cases with many outputs, pre-compute the script using
|
|
400
|
-
* `toOutputScript(address, network)` and pass `{ script, value }` instead.
|
|
401
|
-
*
|
|
402
|
-
* @param outputData - Output data with either `address` or `script`, and `value`
|
|
403
|
-
* @param checkPartialSigs - Whether to check for existing signatures (default: true)
|
|
404
|
-
*/
|
|
405
|
-
addOutput(outputData: PsbtOutputExtended, checkPartialSigs: boolean = true): this {
|
|
410
|
+
public addOutput(outputData: PsbtOutputExtended, checkPartialSigs: boolean = true): this {
|
|
406
411
|
const hasAddress = 'address' in outputData;
|
|
407
412
|
const hasScript = 'script' in outputData;
|
|
408
|
-
if (
|
|
409
|
-
!outputData ||
|
|
410
|
-
outputData.value === undefined ||
|
|
411
|
-
(!hasAddress && !hasScript)
|
|
412
|
-
) {
|
|
413
|
+
if (!outputData || outputData.value === undefined || (!hasAddress && !hasScript)) {
|
|
413
414
|
throw new Error(
|
|
414
415
|
`Invalid arguments for Psbt.addOutput. ` +
|
|
415
416
|
`Requires single object with at least [script or address] and [value]`,
|
|
@@ -426,52 +427,53 @@ export class Psbt {
|
|
|
426
427
|
}
|
|
427
428
|
checkTaprootOutputFields(outputData, outputData, 'addOutput');
|
|
428
429
|
|
|
429
|
-
const c = this.#cache;
|
|
430
430
|
this.data.addOutput(outputData);
|
|
431
|
-
|
|
432
|
-
c.feeRate = undefined;
|
|
433
|
-
c.extractedTx = undefined;
|
|
434
|
-
c.taprootHashCache = undefined;
|
|
431
|
+
this.#cache.invalidate('outputs');
|
|
435
432
|
return this;
|
|
436
433
|
}
|
|
437
434
|
|
|
438
|
-
extractTransaction(
|
|
435
|
+
public extractTransaction(
|
|
436
|
+
disableFeeCheck?: boolean,
|
|
437
|
+
disableOutputChecks?: boolean,
|
|
438
|
+
): Transaction {
|
|
439
439
|
if (disableOutputChecks) {
|
|
440
|
-
(this.data as unknown as { inputs: PsbtInput[] }).inputs = this.data.inputs.filter(
|
|
440
|
+
(this.data as unknown as { inputs: PsbtInput[] }).inputs = this.data.inputs.filter(
|
|
441
|
+
(i) => !i.partialSig,
|
|
442
|
+
);
|
|
441
443
|
}
|
|
442
444
|
|
|
443
445
|
if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
|
|
444
|
-
const c = this.#cache;
|
|
445
446
|
if (!disableFeeCheck) {
|
|
446
|
-
|
|
447
|
+
this.#cache.computeFeeRate(this.data.inputs, disableOutputChecks, txFromBuffer);
|
|
448
|
+
this.#cache.checkFees(this.#opts);
|
|
447
449
|
}
|
|
448
|
-
if (
|
|
449
|
-
const tx =
|
|
450
|
-
|
|
451
|
-
return tx;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
getFeeRate(disableOutputChecks: boolean = false): number {
|
|
455
|
-
return getTxCacheValue(
|
|
456
|
-
'feeRate',
|
|
457
|
-
'fee rate',
|
|
450
|
+
if (this.#cache.extractedTx) return this.#cache.extractedTx;
|
|
451
|
+
const tx = this.#cache.tx.clone();
|
|
452
|
+
this.#cache.finalizeAndComputeAmounts(
|
|
458
453
|
this.data.inputs,
|
|
459
|
-
|
|
454
|
+
tx,
|
|
455
|
+
true,
|
|
460
456
|
disableOutputChecks,
|
|
457
|
+
txFromBuffer,
|
|
461
458
|
);
|
|
459
|
+
return tx;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
public getFeeRate(disableOutputChecks: boolean = false): number {
|
|
463
|
+
return this.#cache.computeFeeRate(this.data.inputs, disableOutputChecks, txFromBuffer);
|
|
462
464
|
}
|
|
463
465
|
|
|
464
|
-
getFee(disableOutputChecks: boolean = false): number {
|
|
465
|
-
return
|
|
466
|
+
public getFee(disableOutputChecks: boolean = false): number {
|
|
467
|
+
return this.#cache.computeFee(this.data.inputs, disableOutputChecks, txFromBuffer);
|
|
466
468
|
}
|
|
467
469
|
|
|
468
|
-
finalizeAllInputs(): this {
|
|
469
|
-
checkForInput(this.data.inputs, 0);
|
|
470
|
+
public finalizeAllInputs(): this {
|
|
471
|
+
checkForInput(this.data.inputs, 0);
|
|
470
472
|
range(this.data.inputs.length).forEach((idx) => this.finalizeInput(idx));
|
|
471
473
|
return this;
|
|
472
474
|
}
|
|
473
475
|
|
|
474
|
-
finalizeInput(
|
|
476
|
+
public finalizeInput(
|
|
475
477
|
inputIndex: number,
|
|
476
478
|
finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc,
|
|
477
479
|
canRunChecks?: boolean,
|
|
@@ -493,7 +495,7 @@ export class Psbt {
|
|
|
493
495
|
);
|
|
494
496
|
}
|
|
495
497
|
|
|
496
|
-
finalizeTaprootInput(
|
|
498
|
+
public finalizeTaprootInput(
|
|
497
499
|
inputIndex: number,
|
|
498
500
|
tapLeafHashToFinalize?: Bytes32,
|
|
499
501
|
finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
|
|
@@ -509,54 +511,53 @@ export class Psbt {
|
|
|
509
511
|
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
|
|
510
512
|
}
|
|
511
513
|
|
|
512
|
-
getInputType(inputIndex: number): AllScriptType {
|
|
514
|
+
public getInputType(inputIndex: number): AllScriptType {
|
|
513
515
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
514
|
-
const script = getScriptFromUtxo(inputIndex, input,
|
|
516
|
+
const script = this.#cache.getScriptFromUtxo(inputIndex, input, txFromBuffer);
|
|
515
517
|
const result = getMeaningfulScript(
|
|
516
518
|
script,
|
|
517
519
|
inputIndex,
|
|
518
520
|
'input',
|
|
519
|
-
input.redeemScript ||
|
|
520
|
-
redeemFromFinalScriptSig(input.finalScriptSig),
|
|
521
|
+
input.redeemScript || this.#cache.redeemFromFinalScriptSig(input.finalScriptSig),
|
|
521
522
|
input.witnessScript ||
|
|
522
|
-
redeemFromFinalWitnessScript(input.finalScriptWitness),
|
|
523
|
+
this.#cache.redeemFromFinalWitnessScript(input.finalScriptWitness),
|
|
523
524
|
);
|
|
524
525
|
const type = result.type === 'raw' ? '' : result.type + '-';
|
|
525
526
|
const mainType = classifyScript(result.meaningfulScript);
|
|
526
527
|
return (type + mainType) as AllScriptType;
|
|
527
528
|
}
|
|
528
529
|
|
|
529
|
-
inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean {
|
|
530
|
+
public inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean {
|
|
530
531
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
531
|
-
return pubkeyInInput(pubkey, input, inputIndex,
|
|
532
|
+
return this.#cache.pubkeyInInput(pubkey, input, inputIndex, txFromBuffer);
|
|
532
533
|
}
|
|
533
534
|
|
|
534
|
-
inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
|
|
535
|
+
public inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
|
|
535
536
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
536
|
-
const derivationIsMine = bip32DerivationIsMine(root);
|
|
537
|
+
const derivationIsMine = this.#lazySigner.bip32DerivationIsMine(root);
|
|
537
538
|
return !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine);
|
|
538
539
|
}
|
|
539
540
|
|
|
540
|
-
outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean {
|
|
541
|
+
public outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean {
|
|
541
542
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
542
|
-
return pubkeyInOutput(pubkey, output, outputIndex
|
|
543
|
+
return this.#cache.pubkeyInOutput(pubkey, output, outputIndex);
|
|
543
544
|
}
|
|
544
545
|
|
|
545
|
-
outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
|
|
546
|
+
public outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
|
|
546
547
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
547
|
-
const derivationIsMine = bip32DerivationIsMine(root);
|
|
548
|
+
const derivationIsMine = this.#lazySigner.bip32DerivationIsMine(root);
|
|
548
549
|
return !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine);
|
|
549
550
|
}
|
|
550
551
|
|
|
551
|
-
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
|
|
552
|
-
checkForInput(this.data.inputs, 0);
|
|
552
|
+
public validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
|
|
553
|
+
checkForInput(this.data.inputs, 0);
|
|
553
554
|
const results = range(this.data.inputs.length).map((idx) =>
|
|
554
555
|
this.validateSignaturesOfInput(idx, validator),
|
|
555
556
|
);
|
|
556
|
-
return results.
|
|
557
|
+
return results.every((res) => res);
|
|
557
558
|
}
|
|
558
559
|
|
|
559
|
-
validateSignaturesOfInput(
|
|
560
|
+
public validateSignaturesOfInput(
|
|
560
561
|
inputIndex: number,
|
|
561
562
|
validator: ValidateSigFunction,
|
|
562
563
|
pubkey?: PublicKey,
|
|
@@ -568,7 +569,10 @@ export class Psbt {
|
|
|
568
569
|
return this.#validateSignaturesOfInput(inputIndex, validator, pubkey);
|
|
569
570
|
}
|
|
570
571
|
|
|
571
|
-
signAllInputsHD(
|
|
572
|
+
public signAllInputsHD(
|
|
573
|
+
hdKeyPair: HDSigner,
|
|
574
|
+
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
575
|
+
): this {
|
|
572
576
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
573
577
|
throw new Error('Need HDSigner to sign input');
|
|
574
578
|
}
|
|
@@ -588,39 +592,35 @@ export class Psbt {
|
|
|
588
592
|
return this;
|
|
589
593
|
}
|
|
590
594
|
|
|
591
|
-
signAllInputsHDAsync(
|
|
595
|
+
public async signAllInputsHDAsync(
|
|
592
596
|
hdKeyPair: HDSigner | HDSignerAsync,
|
|
593
597
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
594
598
|
): Promise<void> {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
+
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
600
|
+
throw new Error('Need HDSigner to sign input');
|
|
601
|
+
}
|
|
599
602
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
resolve();
|
|
619
|
-
});
|
|
620
|
-
});
|
|
603
|
+
const results: boolean[] = [];
|
|
604
|
+
const promises: Array<Promise<void>> = [];
|
|
605
|
+
for (const i of range(this.data.inputs.length)) {
|
|
606
|
+
promises.push(
|
|
607
|
+
this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
|
|
608
|
+
() => {
|
|
609
|
+
results.push(true);
|
|
610
|
+
},
|
|
611
|
+
() => {
|
|
612
|
+
results.push(false);
|
|
613
|
+
},
|
|
614
|
+
),
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
await Promise.all(promises);
|
|
618
|
+
if (results.every((v) => !v)) {
|
|
619
|
+
throw new Error('No inputs were signed');
|
|
620
|
+
}
|
|
621
621
|
}
|
|
622
622
|
|
|
623
|
-
signInputHD(
|
|
623
|
+
public signInputHD(
|
|
624
624
|
inputIndex: number,
|
|
625
625
|
hdKeyPair: HDSigner,
|
|
626
626
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
@@ -628,41 +628,32 @@ export class Psbt {
|
|
|
628
628
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
629
629
|
throw new Error('Need HDSigner to sign input');
|
|
630
630
|
}
|
|
631
|
-
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
631
|
+
const signers = this.#lazySigner.getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
632
632
|
signers.forEach((signer) => this.signInput(inputIndex, signer, sighashTypes));
|
|
633
633
|
return this;
|
|
634
634
|
}
|
|
635
635
|
|
|
636
|
-
signInputHDAsync(
|
|
636
|
+
public async signInputHDAsync(
|
|
637
637
|
inputIndex: number,
|
|
638
638
|
hdKeyPair: HDSigner | HDSignerAsync,
|
|
639
639
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
640
640
|
): Promise<void> {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return Promise.all(promises)
|
|
650
|
-
.then(() => {
|
|
651
|
-
resolve();
|
|
652
|
-
})
|
|
653
|
-
.catch(reject);
|
|
654
|
-
});
|
|
641
|
+
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
642
|
+
throw new Error('Need HDSigner to sign input');
|
|
643
|
+
}
|
|
644
|
+
const signers = this.#lazySigner.getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
645
|
+
const promises = signers.map((signer) =>
|
|
646
|
+
this.signInputAsync(inputIndex, signer, sighashTypes),
|
|
647
|
+
);
|
|
648
|
+
await Promise.all(promises);
|
|
655
649
|
}
|
|
656
650
|
|
|
657
|
-
signAllInputs(
|
|
658
|
-
keyPair: Signer | SignerAlternative | BIP32Interface |
|
|
651
|
+
public signAllInputs(
|
|
652
|
+
keyPair: Signer | SignerAlternative | BIP32Interface | UniversalSigner,
|
|
659
653
|
sighashTypes?: number[],
|
|
660
654
|
): this {
|
|
661
655
|
if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
|
|
662
656
|
|
|
663
|
-
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
664
|
-
// as input information is added, then eventually
|
|
665
|
-
// optimize this method.
|
|
666
657
|
const results: boolean[] = [];
|
|
667
658
|
for (const i of range(this.data.inputs.length)) {
|
|
668
659
|
try {
|
|
@@ -678,43 +669,35 @@ export class Psbt {
|
|
|
678
669
|
return this;
|
|
679
670
|
}
|
|
680
671
|
|
|
681
|
-
signAllInputsAsync(
|
|
682
|
-
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface |
|
|
672
|
+
public async signAllInputsAsync(
|
|
673
|
+
keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | UniversalSigner,
|
|
683
674
|
sighashTypes?: number[],
|
|
684
675
|
): Promise<void> {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
}
|
|
706
|
-
return Promise.all(promises).then(() => {
|
|
707
|
-
if (results.every((v) => !v)) {
|
|
708
|
-
return reject(new Error('No inputs were signed'));
|
|
709
|
-
}
|
|
710
|
-
resolve();
|
|
711
|
-
});
|
|
712
|
-
});
|
|
676
|
+
if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
|
|
677
|
+
|
|
678
|
+
const results: boolean[] = [];
|
|
679
|
+
const promises: Array<Promise<void>> = [];
|
|
680
|
+
for (const [i] of this.data.inputs.entries()) {
|
|
681
|
+
promises.push(
|
|
682
|
+
this.signInputAsync(i, keyPair, sighashTypes).then(
|
|
683
|
+
() => {
|
|
684
|
+
results.push(true);
|
|
685
|
+
},
|
|
686
|
+
() => {
|
|
687
|
+
results.push(false);
|
|
688
|
+
},
|
|
689
|
+
),
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
await Promise.all(promises);
|
|
693
|
+
if (results.every((v) => !v)) {
|
|
694
|
+
throw new Error('No inputs were signed');
|
|
695
|
+
}
|
|
713
696
|
}
|
|
714
697
|
|
|
715
|
-
signInput(
|
|
698
|
+
public signInput(
|
|
716
699
|
inputIndex: number,
|
|
717
|
-
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface |
|
|
700
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
|
|
718
701
|
sighashTypes?: number[],
|
|
719
702
|
): this {
|
|
720
703
|
if (!keyPair || !keyPair.publicKey) {
|
|
@@ -729,9 +712,9 @@ export class Psbt {
|
|
|
729
712
|
return this.#signInput(inputIndex, keyPair, sighashTypes);
|
|
730
713
|
}
|
|
731
714
|
|
|
732
|
-
signTaprootInput(
|
|
715
|
+
public signTaprootInput(
|
|
733
716
|
inputIndex: number,
|
|
734
|
-
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface |
|
|
717
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
|
|
735
718
|
tapLeafHashToSign?: Uint8Array,
|
|
736
719
|
sighashTypes?: number[],
|
|
737
720
|
): this {
|
|
@@ -753,9 +736,16 @@ export class Psbt {
|
|
|
753
736
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
754
737
|
}
|
|
755
738
|
|
|
756
|
-
signInputAsync(
|
|
739
|
+
public signInputAsync(
|
|
757
740
|
inputIndex: number,
|
|
758
|
-
keyPair:
|
|
741
|
+
keyPair:
|
|
742
|
+
| Signer
|
|
743
|
+
| SignerAlternative
|
|
744
|
+
| SignerAsync
|
|
745
|
+
| HDSigner
|
|
746
|
+
| HDSignerAsync
|
|
747
|
+
| BIP32Interface
|
|
748
|
+
| UniversalSigner,
|
|
759
749
|
sighashTypes?: number[],
|
|
760
750
|
): Promise<void> {
|
|
761
751
|
return Promise.resolve().then(() => {
|
|
@@ -775,9 +765,16 @@ export class Psbt {
|
|
|
775
765
|
});
|
|
776
766
|
}
|
|
777
767
|
|
|
778
|
-
signTaprootInputAsync(
|
|
768
|
+
public signTaprootInputAsync(
|
|
779
769
|
inputIndex: number,
|
|
780
|
-
keyPair:
|
|
770
|
+
keyPair:
|
|
771
|
+
| Signer
|
|
772
|
+
| SignerAlternative
|
|
773
|
+
| SignerAsync
|
|
774
|
+
| HDSigner
|
|
775
|
+
| HDSignerAsync
|
|
776
|
+
| BIP32Interface
|
|
777
|
+
| UniversalSigner,
|
|
781
778
|
tapLeafHash?: Uint8Array,
|
|
782
779
|
sighashTypes?: number[],
|
|
783
780
|
): Promise<void> {
|
|
@@ -798,30 +795,29 @@ export class Psbt {
|
|
|
798
795
|
});
|
|
799
796
|
}
|
|
800
797
|
|
|
801
|
-
toBuffer(): Uint8Array {
|
|
798
|
+
public toBuffer(): Uint8Array {
|
|
802
799
|
checkCache(this.#cache);
|
|
803
800
|
return new Uint8Array(this.data.toBuffer());
|
|
804
801
|
}
|
|
805
802
|
|
|
806
|
-
toHex(): string {
|
|
803
|
+
public toHex(): string {
|
|
807
804
|
checkCache(this.#cache);
|
|
808
805
|
return this.data.toHex();
|
|
809
806
|
}
|
|
810
807
|
|
|
811
|
-
toBase64(): string {
|
|
808
|
+
public toBase64(): string {
|
|
812
809
|
checkCache(this.#cache);
|
|
813
810
|
return this.data.toBase64();
|
|
814
811
|
}
|
|
815
812
|
|
|
816
|
-
updateGlobal(updateData: PsbtGlobalUpdate): this {
|
|
813
|
+
public updateGlobal(updateData: PsbtGlobalUpdate): this {
|
|
817
814
|
this.data.updateGlobal(updateData);
|
|
818
815
|
return this;
|
|
819
816
|
}
|
|
820
817
|
|
|
821
|
-
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
|
|
818
|
+
public updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
|
|
822
819
|
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
|
|
823
820
|
checkTaprootInputFields(this.data.inputs[inputIndex]!, updateData, 'updateInput');
|
|
824
|
-
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
825
821
|
const normalizedUpdate = updateData.witnessUtxo
|
|
826
822
|
? {
|
|
827
823
|
...updateData,
|
|
@@ -836,12 +832,16 @@ export class Psbt {
|
|
|
836
832
|
: updateData;
|
|
837
833
|
this.data.updateInput(inputIndex, normalizedUpdate);
|
|
838
834
|
if (updateData.nonWitnessUtxo) {
|
|
839
|
-
|
|
835
|
+
this.#cache.addNonWitnessTxCache(
|
|
836
|
+
this.data.inputs[inputIndex]!,
|
|
837
|
+
inputIndex,
|
|
838
|
+
txFromBuffer,
|
|
839
|
+
);
|
|
840
840
|
}
|
|
841
841
|
return this;
|
|
842
842
|
}
|
|
843
843
|
|
|
844
|
-
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
|
|
844
|
+
public updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
|
|
845
845
|
const outputData = this.data.outputs[outputIndex]!;
|
|
846
846
|
checkTaprootOutputFields(outputData, updateData, 'updateOutput');
|
|
847
847
|
|
|
@@ -849,54 +849,60 @@ export class Psbt {
|
|
|
849
849
|
return this;
|
|
850
850
|
}
|
|
851
851
|
|
|
852
|
-
addUnknownKeyValToGlobal(keyVal: KeyValue): this {
|
|
852
|
+
public addUnknownKeyValToGlobal(keyVal: KeyValue): this {
|
|
853
853
|
this.data.addUnknownKeyValToGlobal(keyVal);
|
|
854
854
|
return this;
|
|
855
855
|
}
|
|
856
856
|
|
|
857
|
-
addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
|
|
857
|
+
public addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
|
|
858
858
|
this.data.addUnknownKeyValToInput(inputIndex, keyVal);
|
|
859
859
|
return this;
|
|
860
860
|
}
|
|
861
861
|
|
|
862
|
-
addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
|
|
862
|
+
public addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
|
|
863
863
|
this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
|
|
864
864
|
return this;
|
|
865
865
|
}
|
|
866
866
|
|
|
867
|
-
clearFinalizedInput(inputIndex: number): this {
|
|
867
|
+
public clearFinalizedInput(inputIndex: number): this {
|
|
868
868
|
this.data.clearFinalizedInput(inputIndex);
|
|
869
869
|
return this;
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
-
checkTaprootHashesForSig(
|
|
872
|
+
public checkTaprootHashesForSig(
|
|
873
873
|
inputIndex: number,
|
|
874
874
|
input: PsbtInput,
|
|
875
|
-
keyPair:
|
|
875
|
+
keyPair:
|
|
876
|
+
| Signer
|
|
877
|
+
| SignerAlternative
|
|
878
|
+
| SignerAsync
|
|
879
|
+
| HDSigner
|
|
880
|
+
| HDSignerAsync
|
|
881
|
+
| TaprootHashCheckSigner
|
|
882
|
+
| BIP32Interface
|
|
883
|
+
| UniversalSigner,
|
|
876
884
|
tapLeafHashToSign?: Uint8Array,
|
|
877
885
|
allowedSighashTypes?: number[],
|
|
878
|
-
): { hash:
|
|
886
|
+
): { hash: MessageHash; leafHash?: Bytes32 }[] {
|
|
879
887
|
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
880
888
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
881
889
|
|
|
882
|
-
const pubkey =
|
|
883
|
-
|
|
884
|
-
|
|
890
|
+
const pubkey =
|
|
891
|
+
keyPair.publicKey instanceof Uint8Array
|
|
892
|
+
? keyPair.publicKey
|
|
893
|
+
: new Uint8Array(keyPair.publicKey);
|
|
885
894
|
|
|
886
|
-
const hashesForSig = getTaprootHashesForSig(
|
|
895
|
+
const hashesForSig = this.#lazySigner.getTaprootHashesForSig(
|
|
887
896
|
inputIndex,
|
|
888
897
|
input,
|
|
889
898
|
this.data.inputs,
|
|
890
899
|
pubkey,
|
|
891
|
-
this.#cache,
|
|
892
900
|
tapLeafHashToSign,
|
|
893
901
|
allowedSighashTypes,
|
|
894
902
|
);
|
|
895
903
|
|
|
896
904
|
if (!hashesForSig || !hashesForSig.length)
|
|
897
|
-
throw new Error(
|
|
898
|
-
`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`,
|
|
899
|
-
);
|
|
905
|
+
throw new Error(`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`);
|
|
900
906
|
|
|
901
907
|
return hashesForSig;
|
|
902
908
|
}
|
|
@@ -907,10 +913,9 @@ export class Psbt {
|
|
|
907
913
|
finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
|
|
908
914
|
canRunChecks: boolean = true,
|
|
909
915
|
): this {
|
|
910
|
-
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
|
|
916
|
+
const { script, isP2SH, isP2WSH, isSegwit } = this.#lazyFinalizer.getScriptFromInput(
|
|
911
917
|
inputIndex,
|
|
912
918
|
input,
|
|
913
|
-
this.#cache,
|
|
914
919
|
);
|
|
915
920
|
if (!script) throw new Error(`No script found for input #${inputIndex}`);
|
|
916
921
|
|
|
@@ -944,7 +949,6 @@ export class Psbt {
|
|
|
944
949
|
if (!input.witnessUtxo)
|
|
945
950
|
throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
|
|
946
951
|
|
|
947
|
-
// Check key spend first. Increased privacy and reduced block space.
|
|
948
952
|
if (input.tapKeySig) {
|
|
949
953
|
const payment = payments.p2tr({
|
|
950
954
|
output: input.witnessUtxo.script as Script,
|
|
@@ -973,17 +977,15 @@ export class Psbt {
|
|
|
973
977
|
pubkey?: PublicKey,
|
|
974
978
|
): boolean {
|
|
975
979
|
const input = this.data.inputs[inputIndex];
|
|
976
|
-
const partialSig =
|
|
980
|
+
const partialSig = input?.partialSig;
|
|
977
981
|
if (!input || !partialSig || partialSig.length < 1)
|
|
978
982
|
throw new Error('No signatures to validate');
|
|
979
983
|
if (typeof validator !== 'function')
|
|
980
984
|
throw new Error('Need validator function to validate signatures');
|
|
981
|
-
const mySigs = pubkey
|
|
982
|
-
? partialSig.filter((sig) => equals(sig.pubkey, pubkey))
|
|
983
|
-
: partialSig;
|
|
985
|
+
const mySigs = pubkey ? partialSig.filter((sig) => equals(sig.pubkey, pubkey)) : partialSig;
|
|
984
986
|
if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
|
|
985
987
|
const results: boolean[] = [];
|
|
986
|
-
let hashCache:
|
|
988
|
+
let hashCache: MessageHash | undefined;
|
|
987
989
|
let scriptCache: Script | undefined;
|
|
988
990
|
let sighashCache: number | undefined;
|
|
989
991
|
for (const pSig of mySigs) {
|
|
@@ -992,12 +994,11 @@ export class Psbt {
|
|
|
992
994
|
const sig = bscript.signature.decode(pSigSignature);
|
|
993
995
|
const { hash, script } =
|
|
994
996
|
sighashCache !== sig.hashType || !hashCache || !scriptCache
|
|
995
|
-
? getHashForSig(
|
|
997
|
+
? this.#lazySigner.getHashForSig(
|
|
996
998
|
inputIndex,
|
|
997
999
|
Object.assign({}, input, {
|
|
998
1000
|
sighashType: sig.hashType,
|
|
999
1001
|
}),
|
|
1000
|
-
this.#cache,
|
|
1001
1002
|
true,
|
|
1002
1003
|
)
|
|
1003
1004
|
: { hash: hashCache, script: scriptCache };
|
|
@@ -1016,8 +1017,8 @@ export class Psbt {
|
|
|
1016
1017
|
pubkey?: PublicKey,
|
|
1017
1018
|
): boolean {
|
|
1018
1019
|
const input = this.data.inputs[inputIndex]!;
|
|
1019
|
-
const tapKeySig =
|
|
1020
|
-
const tapScriptSig =
|
|
1020
|
+
const tapKeySig = input?.tapKeySig;
|
|
1021
|
+
const tapScriptSig = input?.tapScriptSig;
|
|
1021
1022
|
if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
|
|
1022
1023
|
throw new Error('No signatures to validate');
|
|
1023
1024
|
if (typeof validator !== 'function')
|
|
@@ -1025,8 +1026,8 @@ export class Psbt {
|
|
|
1025
1026
|
|
|
1026
1027
|
const xPubkey = pubkey ? toXOnly(pubkey) : undefined;
|
|
1027
1028
|
const allHashses = xPubkey
|
|
1028
|
-
? getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey
|
|
1029
|
-
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs
|
|
1029
|
+
? this.#lazySigner.getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey)
|
|
1030
|
+
: this.#lazySigner.getAllTaprootHashesForSig(inputIndex, input, this.data.inputs);
|
|
1030
1031
|
|
|
1031
1032
|
if (!allHashses.length) throw new Error('No signatures for this pubkey');
|
|
1032
1033
|
|
|
@@ -1036,7 +1037,7 @@ export class Psbt {
|
|
|
1036
1037
|
const isValidTapkeySig = validator(
|
|
1037
1038
|
tapKeyHash.pubkey,
|
|
1038
1039
|
tapKeyHash.hash,
|
|
1039
|
-
trimTaprootSig(tapKeySig),
|
|
1040
|
+
this.#lazySigner.trimTaprootSig(tapKeySig),
|
|
1040
1041
|
);
|
|
1041
1042
|
if (!isValidTapkeySig) return false;
|
|
1042
1043
|
validationResultCount++;
|
|
@@ -1050,7 +1051,7 @@ export class Psbt {
|
|
|
1050
1051
|
const isValidTapScriptSig = validator(
|
|
1051
1052
|
tapSigPubkey,
|
|
1052
1053
|
tapSigHash.hash,
|
|
1053
|
-
trimTaprootSig(tapSig.signature),
|
|
1054
|
+
this.#lazySigner.trimTaprootSig(tapSig.signature),
|
|
1054
1055
|
);
|
|
1055
1056
|
if (!isValidTapScriptSig) return false;
|
|
1056
1057
|
validationResultCount++;
|
|
@@ -1063,18 +1064,18 @@ export class Psbt {
|
|
|
1063
1064
|
|
|
1064
1065
|
#signInput(
|
|
1065
1066
|
inputIndex: number,
|
|
1066
|
-
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface |
|
|
1067
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
|
|
1067
1068
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
1068
1069
|
): this {
|
|
1069
|
-
const pubkey =
|
|
1070
|
-
|
|
1071
|
-
|
|
1070
|
+
const pubkey =
|
|
1071
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1072
|
+
? keyPair.publicKey
|
|
1073
|
+
: new Uint8Array(keyPair.publicKey);
|
|
1072
1074
|
|
|
1073
|
-
const { hash, sighashType } = getHashAndSighashType(
|
|
1075
|
+
const { hash, sighashType } = this.#lazySigner.getHashAndSighashType(
|
|
1074
1076
|
this.data.inputs,
|
|
1075
1077
|
inputIndex,
|
|
1076
1078
|
pubkey,
|
|
1077
|
-
this.#cache,
|
|
1078
1079
|
sighashTypes,
|
|
1079
1080
|
);
|
|
1080
1081
|
|
|
@@ -1097,7 +1098,7 @@ export class Psbt {
|
|
|
1097
1098
|
#signTaprootInput(
|
|
1098
1099
|
inputIndex: number,
|
|
1099
1100
|
input: PsbtInput,
|
|
1100
|
-
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface |
|
|
1101
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
|
|
1101
1102
|
tapLeafHashToSign?: Uint8Array,
|
|
1102
1103
|
allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
1103
1104
|
): this {
|
|
@@ -1110,7 +1111,6 @@ export class Psbt {
|
|
|
1110
1111
|
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
1111
1112
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
1112
1113
|
|
|
1113
|
-
// checkTaprootHashesForSig validates signSchnorr exists
|
|
1114
1114
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
1115
1115
|
inputIndex,
|
|
1116
1116
|
input,
|
|
@@ -1118,7 +1118,7 @@ export class Psbt {
|
|
|
1118
1118
|
tapLeafHashToSign,
|
|
1119
1119
|
allowedSighashTypes,
|
|
1120
1120
|
);
|
|
1121
|
-
const signSchnorr = (keyPair.signSchnorr as (h:
|
|
1121
|
+
const signSchnorr = (keyPair.signSchnorr as (h: MessageHash) => SchnorrSignature).bind(keyPair);
|
|
1122
1122
|
|
|
1123
1123
|
const tapKeySig = hashesForSig
|
|
1124
1124
|
.filter((h) => !h.leafHash)
|
|
@@ -1155,18 +1155,25 @@ export class Psbt {
|
|
|
1155
1155
|
|
|
1156
1156
|
#signInputAsync(
|
|
1157
1157
|
inputIndex: number,
|
|
1158
|
-
keyPair:
|
|
1158
|
+
keyPair:
|
|
1159
|
+
| Signer
|
|
1160
|
+
| SignerAlternative
|
|
1161
|
+
| SignerAsync
|
|
1162
|
+
| HDSigner
|
|
1163
|
+
| HDSignerAsync
|
|
1164
|
+
| BIP32Interface
|
|
1165
|
+
| UniversalSigner,
|
|
1159
1166
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
1160
1167
|
): Promise<void> {
|
|
1161
|
-
const pubkey =
|
|
1162
|
-
|
|
1163
|
-
|
|
1168
|
+
const pubkey =
|
|
1169
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1170
|
+
? keyPair.publicKey
|
|
1171
|
+
: new Uint8Array(keyPair.publicKey);
|
|
1164
1172
|
|
|
1165
|
-
const { hash, sighashType } = getHashAndSighashType(
|
|
1173
|
+
const { hash, sighashType } = this.#lazySigner.getHashAndSighashType(
|
|
1166
1174
|
this.data.inputs,
|
|
1167
1175
|
inputIndex,
|
|
1168
1176
|
pubkey,
|
|
1169
|
-
this.#cache,
|
|
1170
1177
|
sighashTypes,
|
|
1171
1178
|
);
|
|
1172
1179
|
|
|
@@ -1187,7 +1194,14 @@ export class Psbt {
|
|
|
1187
1194
|
async #signTaprootInputAsync(
|
|
1188
1195
|
inputIndex: number,
|
|
1189
1196
|
input: PsbtInput,
|
|
1190
|
-
keyPair:
|
|
1197
|
+
keyPair:
|
|
1198
|
+
| Signer
|
|
1199
|
+
| SignerAlternative
|
|
1200
|
+
| SignerAsync
|
|
1201
|
+
| HDSigner
|
|
1202
|
+
| HDSignerAsync
|
|
1203
|
+
| BIP32Interface
|
|
1204
|
+
| UniversalSigner,
|
|
1191
1205
|
tapLeafHash?: Uint8Array,
|
|
1192
1206
|
sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
1193
1207
|
): Promise<void> {
|
|
@@ -1200,7 +1214,6 @@ export class Psbt {
|
|
|
1200
1214
|
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
1201
1215
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
1202
1216
|
|
|
1203
|
-
// checkTaprootHashesForSig validates signSchnorr exists
|
|
1204
1217
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
1205
1218
|
inputIndex,
|
|
1206
1219
|
input,
|
|
@@ -1209,7 +1222,7 @@ export class Psbt {
|
|
|
1209
1222
|
sighashTypes,
|
|
1210
1223
|
);
|
|
1211
1224
|
const signSchnorr = (
|
|
1212
|
-
keyPair.signSchnorr as (hash:
|
|
1225
|
+
keyPair.signSchnorr as (hash: MessageHash) => SchnorrSignature | Promise<SchnorrSignature>
|
|
1213
1226
|
).bind(keyPair);
|
|
1214
1227
|
|
|
1215
1228
|
type TapSignatureResult = { tapKeySig: Uint8Array } | { tapScriptSig: TapScriptSig[] };
|
|
@@ -1252,783 +1265,3 @@ export class Psbt {
|
|
|
1252
1265
|
}
|
|
1253
1266
|
}
|
|
1254
1267
|
}
|
|
1255
|
-
|
|
1256
|
-
/**
|
|
1257
|
-
* This function is needed to pass to the bip174 base class's fromBuffer.
|
|
1258
|
-
* It takes the "transaction buffer" portion of the psbt buffer and returns a
|
|
1259
|
-
* Transaction (From the bip174 library) interface.
|
|
1260
|
-
*/
|
|
1261
|
-
const transactionFromBuffer: TransactionFromBuffer = (buffer: Uint8Array): ITransaction =>
|
|
1262
|
-
new PsbtTransaction(buffer);
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* This class implements the Transaction interface from bip174 library.
|
|
1266
|
-
* It contains a bitcoinjs-lib Transaction object.
|
|
1267
|
-
*/
|
|
1268
|
-
class PsbtTransaction implements ITransaction {
|
|
1269
|
-
tx: Transaction;
|
|
1270
|
-
|
|
1271
|
-
constructor(buffer: Uint8Array = new Uint8Array([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
|
1272
|
-
this.tx = Transaction.fromBuffer(buffer);
|
|
1273
|
-
checkTxEmpty(this.tx);
|
|
1274
|
-
Object.defineProperty(this, 'tx', {
|
|
1275
|
-
enumerable: false,
|
|
1276
|
-
writable: true,
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
getInputOutputCounts(): {
|
|
1281
|
-
inputCount: number;
|
|
1282
|
-
outputCount: number;
|
|
1283
|
-
} {
|
|
1284
|
-
return {
|
|
1285
|
-
inputCount: this.tx.ins.length,
|
|
1286
|
-
outputCount: this.tx.outs.length,
|
|
1287
|
-
};
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
addInput(input: TransactionInput): void {
|
|
1291
|
-
if (
|
|
1292
|
-
input.hash === undefined ||
|
|
1293
|
-
input.index === undefined ||
|
|
1294
|
-
(!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
|
|
1295
|
-
typeof input.index !== 'number'
|
|
1296
|
-
) {
|
|
1297
|
-
throw new Error('Error adding input.');
|
|
1298
|
-
}
|
|
1299
|
-
const hash = (
|
|
1300
|
-
typeof input.hash === 'string' ? reverse(fromHex(input.hash)) : input.hash
|
|
1301
|
-
) as Bytes32;
|
|
1302
|
-
|
|
1303
|
-
this.tx.addInput(hash, input.index, input.sequence);
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
addOutput(output: TransactionOutput): void {
|
|
1307
|
-
if (
|
|
1308
|
-
output.script === undefined ||
|
|
1309
|
-
output.value === undefined ||
|
|
1310
|
-
!(output.script instanceof Uint8Array) ||
|
|
1311
|
-
typeof output.value !== 'bigint'
|
|
1312
|
-
) {
|
|
1313
|
-
throw new Error('Error adding output.');
|
|
1314
|
-
}
|
|
1315
|
-
this.tx.addOutput(output.script, output.value);
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
toBuffer(): Uint8Array {
|
|
1319
|
-
return this.tx.toBuffer();
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
function canFinalize(input: PsbtInput, script: Uint8Array, scriptType: string): boolean {
|
|
1324
|
-
switch (scriptType) {
|
|
1325
|
-
case 'pubkey':
|
|
1326
|
-
case 'pubkeyhash':
|
|
1327
|
-
case 'witnesspubkeyhash':
|
|
1328
|
-
return hasSigs(1, input.partialSig);
|
|
1329
|
-
case 'multisig': {
|
|
1330
|
-
const p2ms = payments.p2ms({
|
|
1331
|
-
output: script as Script,
|
|
1332
|
-
});
|
|
1333
|
-
if (p2ms.m === undefined) throw new Error('Cannot determine m for multisig');
|
|
1334
|
-
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
|
|
1335
|
-
}
|
|
1336
|
-
case 'nonstandard':
|
|
1337
|
-
return true;
|
|
1338
|
-
default:
|
|
1339
|
-
return false;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
function hasSigs(neededSigs: number, partialSig?: PartialSig[], pubkeys?: Uint8Array[]): boolean {
|
|
1344
|
-
if (!partialSig) return false;
|
|
1345
|
-
let sigs: PartialSig[];
|
|
1346
|
-
if (pubkeys) {
|
|
1347
|
-
sigs = pubkeys
|
|
1348
|
-
.map((pkey) => {
|
|
1349
|
-
const pubkey = compressPubkey(pkey);
|
|
1350
|
-
return partialSig.find((pSig) => equals(pSig.pubkey, pubkey));
|
|
1351
|
-
})
|
|
1352
|
-
.filter((v): v is PartialSig => !!v);
|
|
1353
|
-
} else {
|
|
1354
|
-
sigs = partialSig;
|
|
1355
|
-
}
|
|
1356
|
-
if (sigs.length > neededSigs) throw new Error('Too many signatures');
|
|
1357
|
-
return sigs.length === neededSigs;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
function bip32DerivationIsMine(root: HDSigner): (d: Bip32Derivation) => boolean {
|
|
1361
|
-
return (d: Bip32Derivation): boolean => {
|
|
1362
|
-
const fingerprint = root.fingerprint instanceof Uint8Array
|
|
1363
|
-
? root.fingerprint
|
|
1364
|
-
: new Uint8Array(root.fingerprint);
|
|
1365
|
-
if (!equals(d.masterFingerprint, fingerprint)) return false;
|
|
1366
|
-
const derivedPubkey = root.derivePath(d.path).publicKey;
|
|
1367
|
-
const pubkey = derivedPubkey instanceof Uint8Array ? derivedPubkey : new Uint8Array(derivedPubkey);
|
|
1368
|
-
if (!equals(pubkey, d.pubkey)) return false;
|
|
1369
|
-
return true;
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
|
|
1374
|
-
const feeRate = cache.feeRate || psbt.getFeeRate();
|
|
1375
|
-
if (!cache.extractedTx) throw new Error('Transaction not extracted');
|
|
1376
|
-
const vsize = cache.extractedTx.virtualSize();
|
|
1377
|
-
const satoshis = feeRate * vsize;
|
|
1378
|
-
if (feeRate >= opts.maximumFeeRate) {
|
|
1379
|
-
throw new Error(
|
|
1380
|
-
`Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
|
|
1381
|
-
`fees, which is ${feeRate} satoshi per byte for a transaction ` +
|
|
1382
|
-
`with a VSize of ${vsize} bytes (segwit counted as 0.25 byte per ` +
|
|
1383
|
-
`byte). Use setMaximumFeeRate method to raise your threshold, or ` +
|
|
1384
|
-
`pass true to the first arg of extractTransaction.`,
|
|
1385
|
-
);
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
function getTxCacheValue(
|
|
1390
|
-
key: TxCacheNumberKey,
|
|
1391
|
-
name: string,
|
|
1392
|
-
inputs: PsbtInput[],
|
|
1393
|
-
c: PsbtCache,
|
|
1394
|
-
disableOutputChecks: boolean = false,
|
|
1395
|
-
): number {
|
|
1396
|
-
if (!inputs.every(isFinalized)) throw new Error(`PSBT must be finalized to calculate ${name}`);
|
|
1397
|
-
if (key === 'feeRate' && c.feeRate) return c.feeRate;
|
|
1398
|
-
if (key === 'fee' && c.fee) return c.fee;
|
|
1399
|
-
let tx: Transaction;
|
|
1400
|
-
let mustFinalize = true;
|
|
1401
|
-
if (c.extractedTx) {
|
|
1402
|
-
tx = c.extractedTx;
|
|
1403
|
-
mustFinalize = false;
|
|
1404
|
-
} else {
|
|
1405
|
-
tx = c.tx.clone();
|
|
1406
|
-
}
|
|
1407
|
-
inputFinalizeGetAmts(inputs, tx, c, mustFinalize, disableOutputChecks);
|
|
1408
|
-
const value = key === 'feeRate' ? c.feeRate : c.fee;
|
|
1409
|
-
if (value === undefined) throw new Error(`Failed to calculate ${name}`);
|
|
1410
|
-
return value;
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
export function getFinalScripts(
|
|
1414
|
-
inputIndex: number,
|
|
1415
|
-
input: PsbtInput,
|
|
1416
|
-
script: Script,
|
|
1417
|
-
isSegwit: boolean,
|
|
1418
|
-
isP2SH: boolean,
|
|
1419
|
-
isP2WSH: boolean,
|
|
1420
|
-
canRunChecks: boolean = true,
|
|
1421
|
-
solution?: Uint8Array[],
|
|
1422
|
-
): {
|
|
1423
|
-
finalScriptSig: Script | undefined;
|
|
1424
|
-
finalScriptWitness: Uint8Array | undefined;
|
|
1425
|
-
} {
|
|
1426
|
-
const scriptType = classifyScript(script);
|
|
1427
|
-
if (!canFinalize(input, script, scriptType) && canRunChecks) {
|
|
1428
|
-
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
if (!input.partialSig) throw new Error('Input missing partial signatures');
|
|
1432
|
-
return prepareFinalScripts(
|
|
1433
|
-
script,
|
|
1434
|
-
scriptType,
|
|
1435
|
-
input.partialSig,
|
|
1436
|
-
isSegwit,
|
|
1437
|
-
isP2SH,
|
|
1438
|
-
isP2WSH,
|
|
1439
|
-
solution,
|
|
1440
|
-
);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
export function prepareFinalScripts(
|
|
1444
|
-
script: Uint8Array,
|
|
1445
|
-
scriptType: string,
|
|
1446
|
-
partialSig: PartialSig[],
|
|
1447
|
-
isSegwit: boolean,
|
|
1448
|
-
isP2SH: boolean,
|
|
1449
|
-
isP2WSH: boolean,
|
|
1450
|
-
solution?: Uint8Array[],
|
|
1451
|
-
): {
|
|
1452
|
-
finalScriptSig: Script | undefined;
|
|
1453
|
-
finalScriptWitness: Uint8Array | undefined;
|
|
1454
|
-
} {
|
|
1455
|
-
let finalScriptSig: Script | undefined;
|
|
1456
|
-
let finalScriptWitness: Uint8Array | undefined;
|
|
1457
|
-
|
|
1458
|
-
// Wow, the payments API is very handy
|
|
1459
|
-
const payment: payments.Payment = getPayment(script, scriptType, partialSig);
|
|
1460
|
-
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment } as P2WSHPayment);
|
|
1461
|
-
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment } as P2SHPayment);
|
|
1462
|
-
|
|
1463
|
-
if (isSegwit) {
|
|
1464
|
-
if (p2wsh && p2wsh.witness) {
|
|
1465
|
-
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
|
|
1466
|
-
} else if (payment && payment.witness) {
|
|
1467
|
-
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
1468
|
-
} else {
|
|
1469
|
-
// nonstandard segwit script
|
|
1470
|
-
finalScriptWitness = witnessStackToScriptWitness(solution ?? [new Uint8Array([0x00])]);
|
|
1471
|
-
}
|
|
1472
|
-
if (p2sh) {
|
|
1473
|
-
finalScriptSig = p2sh?.input as Script | undefined;
|
|
1474
|
-
}
|
|
1475
|
-
} else {
|
|
1476
|
-
if (p2sh) {
|
|
1477
|
-
finalScriptSig = p2sh?.input as Script | undefined;
|
|
1478
|
-
} else {
|
|
1479
|
-
if (!payment) {
|
|
1480
|
-
finalScriptSig = (
|
|
1481
|
-
Array.isArray(solution) && solution[0] ? solution[0] : new Uint8Array([0x01])
|
|
1482
|
-
) as Script;
|
|
1483
|
-
} else {
|
|
1484
|
-
finalScriptSig = payment.input as Script | undefined;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
return {
|
|
1489
|
-
finalScriptSig,
|
|
1490
|
-
finalScriptWitness,
|
|
1491
|
-
};
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
function getHashAndSighashType(
|
|
1495
|
-
inputs: PsbtInput[],
|
|
1496
|
-
inputIndex: number,
|
|
1497
|
-
pubkey: Uint8Array,
|
|
1498
|
-
cache: PsbtCache,
|
|
1499
|
-
sighashTypes: number[],
|
|
1500
|
-
): {
|
|
1501
|
-
hash: Bytes32;
|
|
1502
|
-
sighashType: number;
|
|
1503
|
-
} {
|
|
1504
|
-
const input = checkForInput(inputs, inputIndex);
|
|
1505
|
-
const { hash, sighashType, script } = getHashForSig(
|
|
1506
|
-
inputIndex,
|
|
1507
|
-
input,
|
|
1508
|
-
cache,
|
|
1509
|
-
false,
|
|
1510
|
-
sighashTypes,
|
|
1511
|
-
);
|
|
1512
|
-
|
|
1513
|
-
checkScriptForPubkey(pubkey as PublicKey, script, 'sign');
|
|
1514
|
-
return {
|
|
1515
|
-
hash,
|
|
1516
|
-
sighashType,
|
|
1517
|
-
};
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
function getHashForSig(
|
|
1521
|
-
inputIndex: number,
|
|
1522
|
-
input: PsbtInput,
|
|
1523
|
-
cache: PsbtCache,
|
|
1524
|
-
forValidate: boolean,
|
|
1525
|
-
sighashTypes?: number[],
|
|
1526
|
-
): {
|
|
1527
|
-
script: Script;
|
|
1528
|
-
hash: Bytes32;
|
|
1529
|
-
sighashType: number;
|
|
1530
|
-
} {
|
|
1531
|
-
const unsignedTx = cache.tx;
|
|
1532
|
-
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
|
1533
|
-
checkSighashTypeAllowed(sighashType, sighashTypes);
|
|
1534
|
-
|
|
1535
|
-
let hash: Bytes32;
|
|
1536
|
-
let prevout: Output;
|
|
1537
|
-
|
|
1538
|
-
if (input.nonWitnessUtxo) {
|
|
1539
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1540
|
-
|
|
1541
|
-
const prevoutHash = unsignedTx.ins[inputIndex]!.hash;
|
|
1542
|
-
const utxoHash = nonWitnessUtxoTx.getHash();
|
|
1543
|
-
|
|
1544
|
-
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
|
|
1545
|
-
if (!equals(prevoutHash, utxoHash)) {
|
|
1546
|
-
throw new Error(
|
|
1547
|
-
`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
|
|
1548
|
-
);
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
const prevoutIndex = unsignedTx.ins[inputIndex]!.index;
|
|
1552
|
-
prevout = nonWitnessUtxoTx.outs[prevoutIndex]!;
|
|
1553
|
-
} else if (input.witnessUtxo) {
|
|
1554
|
-
prevout = {
|
|
1555
|
-
script: input.witnessUtxo.script as Script,
|
|
1556
|
-
value: input.witnessUtxo.value as Satoshi,
|
|
1557
|
-
};
|
|
1558
|
-
} else {
|
|
1559
|
-
throw new Error('Need a Utxo input item for signing');
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
const { meaningfulScript, type } = getMeaningfulScript(
|
|
1563
|
-
prevout.script,
|
|
1564
|
-
inputIndex,
|
|
1565
|
-
'input',
|
|
1566
|
-
input.redeemScript,
|
|
1567
|
-
input.witnessScript,
|
|
1568
|
-
);
|
|
1569
|
-
|
|
1570
|
-
const script = meaningfulScript as Script;
|
|
1571
|
-
|
|
1572
|
-
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
|
|
1573
|
-
hash = unsignedTx.hashForWitnessV0(
|
|
1574
|
-
inputIndex,
|
|
1575
|
-
script,
|
|
1576
|
-
prevout.value,
|
|
1577
|
-
sighashType,
|
|
1578
|
-
);
|
|
1579
|
-
} else if (isP2WPKH(meaningfulScript)) {
|
|
1580
|
-
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1581
|
-
const p2pkhPayment = payments.p2pkh({
|
|
1582
|
-
hash: meaningfulScript.subarray(2) as Bytes20,
|
|
1583
|
-
});
|
|
1584
|
-
if (!p2pkhPayment.output) throw new Error('Unable to create signing script');
|
|
1585
|
-
hash = unsignedTx.hashForWitnessV0(
|
|
1586
|
-
inputIndex,
|
|
1587
|
-
p2pkhPayment.output as Script,
|
|
1588
|
-
prevout.value,
|
|
1589
|
-
sighashType,
|
|
1590
|
-
);
|
|
1591
|
-
} else {
|
|
1592
|
-
// non-segwit
|
|
1593
|
-
if (input.nonWitnessUtxo === undefined && !cache.unsafeSignNonSegwit)
|
|
1594
|
-
throw new Error(
|
|
1595
|
-
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
|
1596
|
-
toHex(meaningfulScript),
|
|
1597
|
-
);
|
|
1598
|
-
if (!forValidate && cache.unsafeSignNonSegwit)
|
|
1599
|
-
console.warn(
|
|
1600
|
-
'Warning: Signing non-segwit inputs without the full parent transaction ' +
|
|
1601
|
-
'means there is a chance that a miner could feed you incorrect information ' +
|
|
1602
|
-
"to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
|
|
1603
|
-
'(TransactionBuilder - now removed) when signing non-segwit scripts. You are not ' +
|
|
1604
|
-
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
|
|
1605
|
-
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
|
|
1606
|
-
'*********************',
|
|
1607
|
-
);
|
|
1608
|
-
hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
return {
|
|
1612
|
-
script,
|
|
1613
|
-
sighashType,
|
|
1614
|
-
hash,
|
|
1615
|
-
};
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
function getAllTaprootHashesForSig(
|
|
1619
|
-
inputIndex: number,
|
|
1620
|
-
input: PsbtInput,
|
|
1621
|
-
inputs: PsbtInput[],
|
|
1622
|
-
cache: PsbtCache,
|
|
1623
|
-
): { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] {
|
|
1624
|
-
const allPublicKeys: Uint8Array[] = [];
|
|
1625
|
-
if (input.tapInternalKey) {
|
|
1626
|
-
const key = getPrevoutTaprootKey(inputIndex, input, cache);
|
|
1627
|
-
if (key) {
|
|
1628
|
-
allPublicKeys.push(key);
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
if (input.tapScriptSig) {
|
|
1633
|
-
const tapScriptPubkeys = input.tapScriptSig.map((tss) => tss.pubkey);
|
|
1634
|
-
allPublicKeys.push(...tapScriptPubkeys);
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
const allHashes = allPublicKeys.map((pubicKey) =>
|
|
1638
|
-
getTaprootHashesForSig(inputIndex, input, inputs, pubicKey, cache),
|
|
1639
|
-
);
|
|
1640
|
-
|
|
1641
|
-
return allHashes.flat();
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
function getPrevoutTaprootKey(
|
|
1645
|
-
inputIndex: number,
|
|
1646
|
-
input: PsbtInput,
|
|
1647
|
-
cache: PsbtCache,
|
|
1648
|
-
): XOnlyPublicKey | null {
|
|
1649
|
-
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
1650
|
-
return isP2TR(script) ? script.subarray(2, 34) as XOnlyPublicKey : null;
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
function trimTaprootSig(signature: Uint8Array): Uint8Array {
|
|
1654
|
-
return signature.length === 64 ? signature : signature.subarray(0, 64);
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
function getTaprootHashesForSig(
|
|
1658
|
-
inputIndex: number,
|
|
1659
|
-
input: PsbtInput,
|
|
1660
|
-
inputs: PsbtInput[],
|
|
1661
|
-
pubkey: Uint8Array,
|
|
1662
|
-
cache: PsbtCache,
|
|
1663
|
-
tapLeafHashToSign?: Uint8Array,
|
|
1664
|
-
allowedSighashTypes?: number[],
|
|
1665
|
-
): { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] {
|
|
1666
|
-
const unsignedTx = cache.tx;
|
|
1667
|
-
|
|
1668
|
-
const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
|
|
1669
|
-
checkSighashTypeAllowed(sighashType, allowedSighashTypes);
|
|
1670
|
-
|
|
1671
|
-
if (!cache.prevOuts) {
|
|
1672
|
-
const prevOuts = inputs.map((i, index) =>
|
|
1673
|
-
getScriptAndAmountFromUtxo(index, i, cache),
|
|
1674
|
-
);
|
|
1675
|
-
cache.prevOuts = prevOuts;
|
|
1676
|
-
cache.signingScripts = prevOuts.map((o) => o.script);
|
|
1677
|
-
cache.values = prevOuts.map((o) => o.value);
|
|
1678
|
-
}
|
|
1679
|
-
const signingScripts = cache.signingScripts as readonly Script[];
|
|
1680
|
-
const values = cache.values as readonly Satoshi[];
|
|
1681
|
-
|
|
1682
|
-
// Compute taproot hash cache once for all inputs (O(n) -> O(1) per input)
|
|
1683
|
-
if (!cache.taprootHashCache) {
|
|
1684
|
-
cache.taprootHashCache = unsignedTx.getTaprootHashCache(signingScripts, values);
|
|
1685
|
-
}
|
|
1686
|
-
const taprootCache = cache.taprootHashCache;
|
|
1687
|
-
|
|
1688
|
-
const hashes: { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] = [];
|
|
1689
|
-
if (input.tapInternalKey && !tapLeafHashToSign) {
|
|
1690
|
-
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || new Uint8Array(0);
|
|
1691
|
-
if (equals(toXOnly(pubkey as PublicKey), outputKey)) {
|
|
1692
|
-
const tapKeyHash = unsignedTx.hashForWitnessV1(
|
|
1693
|
-
inputIndex,
|
|
1694
|
-
signingScripts,
|
|
1695
|
-
values,
|
|
1696
|
-
sighashType,
|
|
1697
|
-
undefined,
|
|
1698
|
-
undefined,
|
|
1699
|
-
taprootCache,
|
|
1700
|
-
);
|
|
1701
|
-
hashes.push({ pubkey: pubkey as PublicKey, hash: tapKeyHash });
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
const tapLeafHashes = (input.tapLeafScript || [])
|
|
1706
|
-
.filter((tapLeaf) => pubkeyInScript(pubkey, tapLeaf.script))
|
|
1707
|
-
.map((tapLeaf) => {
|
|
1708
|
-
const hash = tapleafHash({
|
|
1709
|
-
output: tapLeaf.script,
|
|
1710
|
-
version: tapLeaf.leafVersion,
|
|
1711
|
-
});
|
|
1712
|
-
return Object.assign({ hash }, tapLeaf);
|
|
1713
|
-
})
|
|
1714
|
-
.filter((tapLeaf) => !tapLeafHashToSign || equals(tapLeafHashToSign, tapLeaf.hash))
|
|
1715
|
-
.map((tapLeaf) => {
|
|
1716
|
-
const tapScriptHash = unsignedTx.hashForWitnessV1(
|
|
1717
|
-
inputIndex,
|
|
1718
|
-
signingScripts,
|
|
1719
|
-
values,
|
|
1720
|
-
sighashType,
|
|
1721
|
-
tapLeaf.hash as Bytes32,
|
|
1722
|
-
undefined,
|
|
1723
|
-
taprootCache,
|
|
1724
|
-
);
|
|
1725
|
-
|
|
1726
|
-
return {
|
|
1727
|
-
pubkey: pubkey as PublicKey,
|
|
1728
|
-
hash: tapScriptHash,
|
|
1729
|
-
leafHash: tapLeaf.hash as Bytes32,
|
|
1730
|
-
};
|
|
1731
|
-
});
|
|
1732
|
-
|
|
1733
|
-
return hashes.concat(tapLeafHashes);
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
function checkSighashTypeAllowed(sighashType: number, sighashTypes?: number[]): void {
|
|
1737
|
-
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
|
|
1738
|
-
const str = sighashTypeToString(sighashType);
|
|
1739
|
-
throw new Error(
|
|
1740
|
-
`Sighash type is not allowed. Retry the sign method passing the ` +
|
|
1741
|
-
`sighashTypes array of whitelisted types. Sighash type: ${str}`,
|
|
1742
|
-
);
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
function getPayment(
|
|
1747
|
-
script: Uint8Array,
|
|
1748
|
-
scriptType: string,
|
|
1749
|
-
partialSig: PartialSig[],
|
|
1750
|
-
): payments.Payment {
|
|
1751
|
-
const scriptBranded = script as Script;
|
|
1752
|
-
switch (scriptType) {
|
|
1753
|
-
case 'multisig': {
|
|
1754
|
-
const sigs = getSortedSigs(script, partialSig);
|
|
1755
|
-
return payments.p2ms({
|
|
1756
|
-
output: scriptBranded,
|
|
1757
|
-
signatures: sigs as Signature[],
|
|
1758
|
-
});
|
|
1759
|
-
}
|
|
1760
|
-
case 'pubkey':
|
|
1761
|
-
return payments.p2pk({
|
|
1762
|
-
output: scriptBranded,
|
|
1763
|
-
signature: partialSig[0]!.signature as Signature,
|
|
1764
|
-
});
|
|
1765
|
-
case 'pubkeyhash':
|
|
1766
|
-
return payments.p2pkh({
|
|
1767
|
-
output: scriptBranded,
|
|
1768
|
-
pubkey: partialSig[0]!.pubkey as PublicKey,
|
|
1769
|
-
signature: partialSig[0]!.signature as Signature,
|
|
1770
|
-
});
|
|
1771
|
-
case 'witnesspubkeyhash':
|
|
1772
|
-
return payments.p2wpkh({
|
|
1773
|
-
output: scriptBranded,
|
|
1774
|
-
pubkey: partialSig[0]!.pubkey as PublicKey,
|
|
1775
|
-
signature: partialSig[0]!.signature as Signature,
|
|
1776
|
-
});
|
|
1777
|
-
default:
|
|
1778
|
-
throw new Error(`Unknown script type: ${scriptType}`);
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
function getScriptFromInput(
|
|
1783
|
-
inputIndex: number,
|
|
1784
|
-
input: PsbtInput,
|
|
1785
|
-
cache: PsbtCache,
|
|
1786
|
-
): GetScriptReturn {
|
|
1787
|
-
const unsignedTx = cache.tx;
|
|
1788
|
-
const res: GetScriptReturn = {
|
|
1789
|
-
script: null,
|
|
1790
|
-
isSegwit: false,
|
|
1791
|
-
isP2SH: false,
|
|
1792
|
-
isP2WSH: false,
|
|
1793
|
-
};
|
|
1794
|
-
res.isP2SH = !!input.redeemScript;
|
|
1795
|
-
res.isP2WSH = !!input.witnessScript;
|
|
1796
|
-
if (input.witnessScript) {
|
|
1797
|
-
res.script = input.witnessScript as Script;
|
|
1798
|
-
} else if (input.redeemScript) {
|
|
1799
|
-
res.script = input.redeemScript as Script;
|
|
1800
|
-
} else {
|
|
1801
|
-
if (input.nonWitnessUtxo) {
|
|
1802
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1803
|
-
const prevoutIndex = unsignedTx.ins[inputIndex]!.index;
|
|
1804
|
-
res.script = nonWitnessUtxoTx.outs[prevoutIndex]!.script;
|
|
1805
|
-
} else if (input.witnessUtxo) {
|
|
1806
|
-
res.script = input.witnessUtxo.script as Script;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
if (input.witnessScript || (res.script && isP2WPKH(res.script))) {
|
|
1811
|
-
res.isSegwit = true;
|
|
1812
|
-
} else {
|
|
1813
|
-
try {
|
|
1814
|
-
const output = res.script;
|
|
1815
|
-
if (!output) throw new TypeError('Invalid script for segwit address');
|
|
1816
|
-
|
|
1817
|
-
res.isSegwit = isUnknownSegwitVersion(output);
|
|
1818
|
-
} catch (e) {}
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
return res;
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
function getSignersFromHD<T extends HDSigner | HDSignerAsync>(
|
|
1825
|
-
inputIndex: number,
|
|
1826
|
-
inputs: PsbtInput[],
|
|
1827
|
-
hdKeyPair: T,
|
|
1828
|
-
): T[] {
|
|
1829
|
-
const input = checkForInput(inputs, inputIndex);
|
|
1830
|
-
if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
|
|
1831
|
-
throw new Error('Need bip32Derivation to sign with HD');
|
|
1832
|
-
}
|
|
1833
|
-
const myDerivations = input.bip32Derivation
|
|
1834
|
-
.map((bipDv) => {
|
|
1835
|
-
if (equals(bipDv.masterFingerprint, hdKeyPair.fingerprint)) {
|
|
1836
|
-
return bipDv;
|
|
1837
|
-
} else {
|
|
1838
|
-
return;
|
|
1839
|
-
}
|
|
1840
|
-
})
|
|
1841
|
-
.filter((v) => !!v);
|
|
1842
|
-
if (myDerivations.length === 0) {
|
|
1843
|
-
throw new Error(
|
|
1844
|
-
'Need one bip32Derivation masterFingerprint to match the HDSigner fingerprint',
|
|
1845
|
-
);
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
return myDerivations.map((bipDv) => {
|
|
1849
|
-
const node = hdKeyPair.derivePath(bipDv.path) as T;
|
|
1850
|
-
if (!equals(bipDv.pubkey, node.publicKey)) {
|
|
1851
|
-
throw new Error('pubkey did not match bip32Derivation');
|
|
1852
|
-
}
|
|
1853
|
-
return node;
|
|
1854
|
-
});
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
function getSortedSigs(script: Uint8Array, partialSig: PartialSig[]): Uint8Array[] {
|
|
1858
|
-
const p2ms = payments.p2ms({ output: script as Script });
|
|
1859
|
-
if (!p2ms.pubkeys) throw new Error('Cannot extract pubkeys from multisig script');
|
|
1860
|
-
// for each pubkey in order of p2ms script
|
|
1861
|
-
const result: Uint8Array[] = [];
|
|
1862
|
-
for (const pk of p2ms.pubkeys) {
|
|
1863
|
-
// filter partialSig array by pubkey being equal
|
|
1864
|
-
const matched = partialSig.filter((ps) => {
|
|
1865
|
-
return equals(ps.pubkey, pk);
|
|
1866
|
-
})[0];
|
|
1867
|
-
if (matched) {
|
|
1868
|
-
result.push(new Uint8Array(matched.signature));
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
return result;
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
function addNonWitnessTxCache(cache: PsbtCache, input: PsbtInput, inputIndex: number): void {
|
|
1875
|
-
if (!input.nonWitnessUtxo) throw new Error('nonWitnessUtxo is required');
|
|
1876
|
-
// Prevent prototype pollution - ensure input is a valid object
|
|
1877
|
-
if (input === null || input === Object.prototype) {
|
|
1878
|
-
throw new Error('Invalid input object');
|
|
1879
|
-
}
|
|
1880
|
-
const nonWitnessUtxoBuf = input.nonWitnessUtxo;
|
|
1881
|
-
cache.nonWitnessUtxoBufCache[inputIndex] = nonWitnessUtxoBuf;
|
|
1882
|
-
cache.nonWitnessUtxoTxCache[inputIndex] = Transaction.fromBuffer(nonWitnessUtxoBuf);
|
|
1883
|
-
|
|
1884
|
-
const self = cache;
|
|
1885
|
-
const selfIndex = inputIndex;
|
|
1886
|
-
delete input.nonWitnessUtxo;
|
|
1887
|
-
// Using Reflect.defineProperty to avoid prototype pollution concerns
|
|
1888
|
-
Reflect.defineProperty(input, 'nonWitnessUtxo', {
|
|
1889
|
-
enumerable: true,
|
|
1890
|
-
get(): Uint8Array {
|
|
1891
|
-
const buf = self.nonWitnessUtxoBufCache[selfIndex];
|
|
1892
|
-
const txCache = self.nonWitnessUtxoTxCache[selfIndex];
|
|
1893
|
-
if (buf !== undefined) {
|
|
1894
|
-
return buf;
|
|
1895
|
-
} else {
|
|
1896
|
-
const newBuf = txCache!.toBuffer();
|
|
1897
|
-
self.nonWitnessUtxoBufCache[selfIndex] = newBuf;
|
|
1898
|
-
return newBuf;
|
|
1899
|
-
}
|
|
1900
|
-
},
|
|
1901
|
-
set(data: Uint8Array): void {
|
|
1902
|
-
self.nonWitnessUtxoBufCache[selfIndex] = data;
|
|
1903
|
-
},
|
|
1904
|
-
});
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
|
-
function inputFinalizeGetAmts(
|
|
1908
|
-
inputs: PsbtInput[],
|
|
1909
|
-
tx: Transaction,
|
|
1910
|
-
cache: PsbtCache,
|
|
1911
|
-
mustFinalize: boolean,
|
|
1912
|
-
disableOutputChecks?: boolean,
|
|
1913
|
-
): void {
|
|
1914
|
-
let inputAmount = 0n;
|
|
1915
|
-
inputs.forEach((input, idx) => {
|
|
1916
|
-
if (mustFinalize && input.finalScriptSig)
|
|
1917
|
-
tx.ins[idx]!.script = input.finalScriptSig as Script;
|
|
1918
|
-
if (mustFinalize && input.finalScriptWitness) {
|
|
1919
|
-
tx.ins[idx]!.witness = scriptWitnessToWitnessStack(input.finalScriptWitness);
|
|
1920
|
-
}
|
|
1921
|
-
if (input.witnessUtxo) {
|
|
1922
|
-
inputAmount += input.witnessUtxo.value;
|
|
1923
|
-
} else if (input.nonWitnessUtxo) {
|
|
1924
|
-
const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
|
|
1925
|
-
const vout = tx.ins[idx]!.index;
|
|
1926
|
-
const out = nwTx.outs[vout]!;
|
|
1927
|
-
inputAmount += out.value;
|
|
1928
|
-
}
|
|
1929
|
-
});
|
|
1930
|
-
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
|
|
1931
|
-
const fee = inputAmount - outputAmount;
|
|
1932
|
-
if (!disableOutputChecks) {
|
|
1933
|
-
if (fee < 0n) {
|
|
1934
|
-
throw new Error(
|
|
1935
|
-
`Outputs are spending more than Inputs ${inputAmount} < ${outputAmount}`,
|
|
1936
|
-
);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
const bytes = tx.virtualSize();
|
|
1940
|
-
cache.fee = Number(fee);
|
|
1941
|
-
cache.extractedTx = tx;
|
|
1942
|
-
cache.feeRate = Math.floor(Number(fee) / bytes);
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
function nonWitnessUtxoTxFromCache(
|
|
1946
|
-
cache: PsbtCache,
|
|
1947
|
-
input: PsbtInput,
|
|
1948
|
-
inputIndex: number,
|
|
1949
|
-
): Transaction {
|
|
1950
|
-
const c = cache.nonWitnessUtxoTxCache;
|
|
1951
|
-
if (!c[inputIndex]) {
|
|
1952
|
-
addNonWitnessTxCache(cache, input, inputIndex);
|
|
1953
|
-
}
|
|
1954
|
-
return c[inputIndex]!;
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
function getScriptFromUtxo(inputIndex: number, input: PsbtInput, cache: PsbtCache): Script {
|
|
1958
|
-
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
1959
|
-
return script;
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
function getScriptAndAmountFromUtxo(
|
|
1963
|
-
inputIndex: number,
|
|
1964
|
-
input: PsbtInput,
|
|
1965
|
-
cache: PsbtCache,
|
|
1966
|
-
): { script: Script; value: Satoshi } {
|
|
1967
|
-
if (input.witnessUtxo !== undefined) {
|
|
1968
|
-
return {
|
|
1969
|
-
script: input.witnessUtxo.script as Script,
|
|
1970
|
-
value: input.witnessUtxo.value as Satoshi,
|
|
1971
|
-
};
|
|
1972
|
-
} else if (input.nonWitnessUtxo !== undefined) {
|
|
1973
|
-
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1974
|
-
const o = nonWitnessUtxoTx.outs[cache.tx.ins[inputIndex]!.index]!;
|
|
1975
|
-
return { script: o.script, value: o.value };
|
|
1976
|
-
} else {
|
|
1977
|
-
throw new Error("Can't find pubkey in input without Utxo data");
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
function pubkeyInInput(
|
|
1982
|
-
pubkey: PublicKey,
|
|
1983
|
-
input: PsbtInput,
|
|
1984
|
-
inputIndex: number,
|
|
1985
|
-
cache: PsbtCache,
|
|
1986
|
-
): boolean {
|
|
1987
|
-
const script = getScriptFromUtxo(inputIndex, input, cache);
|
|
1988
|
-
const { meaningfulScript } = getMeaningfulScript(
|
|
1989
|
-
script,
|
|
1990
|
-
inputIndex,
|
|
1991
|
-
'input',
|
|
1992
|
-
input.redeemScript,
|
|
1993
|
-
input.witnessScript,
|
|
1994
|
-
);
|
|
1995
|
-
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
function pubkeyInOutput(
|
|
1999
|
-
pubkey: PublicKey,
|
|
2000
|
-
output: PsbtOutput,
|
|
2001
|
-
outputIndex: number,
|
|
2002
|
-
cache: PsbtCache,
|
|
2003
|
-
): boolean {
|
|
2004
|
-
const script = cache.tx.outs[outputIndex]!.script;
|
|
2005
|
-
const { meaningfulScript } = getMeaningfulScript(
|
|
2006
|
-
script,
|
|
2007
|
-
outputIndex,
|
|
2008
|
-
'output',
|
|
2009
|
-
output.redeemScript,
|
|
2010
|
-
output.witnessScript,
|
|
2011
|
-
);
|
|
2012
|
-
return pubkeyInScript(pubkey, meaningfulScript);
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
function redeemFromFinalScriptSig(finalScript: Uint8Array | undefined): Uint8Array | undefined {
|
|
2016
|
-
if (!finalScript) return;
|
|
2017
|
-
const decomp = bscript.decompile(finalScript);
|
|
2018
|
-
if (!decomp) return;
|
|
2019
|
-
const lastItem = decomp[decomp.length - 1]!;
|
|
2020
|
-
if (!(lastItem instanceof Uint8Array) || isPubkeyLike(lastItem) || isSigLike(lastItem)) return;
|
|
2021
|
-
const sDecomp = bscript.decompile(lastItem);
|
|
2022
|
-
if (!sDecomp) return;
|
|
2023
|
-
return lastItem;
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
function redeemFromFinalWitnessScript(finalScript: Uint8Array | undefined): Uint8Array | undefined {
|
|
2027
|
-
if (!finalScript) return;
|
|
2028
|
-
const decomp = scriptWitnessToWitnessStack(finalScript);
|
|
2029
|
-
const lastItem = decomp[decomp.length - 1]!;
|
|
2030
|
-
if (isPubkeyLike(lastItem)) return;
|
|
2031
|
-
const sDecomp = bscript.decompile(lastItem);
|
|
2032
|
-
if (!sDecomp) return;
|
|
2033
|
-
return lastItem;
|
|
2034
|
-
}
|