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