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