@btc-vision/bitcoin 6.5.6 → 7.0.0-alpha.1
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/HOW_TO_WRITE_GOOD_CODE.md +2436 -0
- package/benchmark/psbt-2000-inputs.bench.ts +178 -0
- package/benchmark/signing.bench.ts +147 -0
- package/browser/address.d.ts +57 -10
- package/browser/address.d.ts.map +1 -0
- package/browser/bech32utils.d.ts +9 -1
- package/browser/bech32utils.d.ts.map +1 -0
- package/browser/bip66.d.ts +11 -6
- package/browser/bip66.d.ts.map +1 -0
- package/browser/block.d.ts +117 -11
- package/browser/block.d.ts.map +1 -0
- package/browser/branded.d.ts +20 -0
- package/browser/branded.d.ts.map +1 -0
- package/browser/crypto/crypto.d.ts +1 -0
- package/browser/crypto/crypto.d.ts.map +1 -0
- package/browser/crypto.d.ts +46 -7
- package/browser/crypto.d.ts.map +1 -0
- package/browser/ecc/context.d.ts +129 -0
- package/browser/ecc/context.d.ts.map +1 -0
- package/browser/ecc/index.d.ts +11 -0
- package/browser/ecc/index.d.ts.map +1 -0
- package/browser/ecc/types.d.ts +128 -0
- package/browser/ecc/types.d.ts.map +1 -0
- package/browser/ecpair.d.ts +99 -0
- package/browser/errors.d.ts +124 -0
- package/browser/errors.d.ts.map +1 -0
- package/browser/index.d.ts +32 -5
- package/browser/index.d.ts.map +1 -0
- package/browser/index.js +12477 -101
- package/browser/io/BinaryReader.d.ts +276 -0
- package/browser/io/BinaryReader.d.ts.map +1 -0
- package/browser/io/BinaryWriter.d.ts +391 -0
- package/browser/io/BinaryWriter.d.ts.map +1 -0
- package/browser/io/MemoryPool.d.ts +220 -0
- package/browser/io/MemoryPool.d.ts.map +1 -0
- package/browser/io/base64.d.ts +13 -0
- package/browser/io/base64.d.ts.map +1 -0
- package/browser/io/hex.d.ts +67 -0
- package/browser/io/hex.d.ts.map +1 -0
- package/browser/io/index.d.ts +17 -0
- package/browser/io/index.d.ts.map +1 -0
- package/browser/io/utils.d.ts +199 -0
- package/browser/io/utils.d.ts.map +1 -0
- package/browser/merkle.d.ts +10 -1
- package/browser/merkle.d.ts.map +1 -0
- package/browser/networks.d.ts +70 -9
- package/browser/networks.d.ts.map +1 -0
- package/browser/opcodes.d.ts +1 -0
- package/browser/opcodes.d.ts.map +1 -0
- package/browser/payments/bip341.d.ts +35 -9
- package/browser/payments/bip341.d.ts.map +1 -0
- package/browser/payments/embed.d.ts +112 -1
- package/browser/payments/embed.d.ts.map +1 -0
- package/browser/payments/index.d.ts +17 -10
- package/browser/payments/index.d.ts.map +1 -0
- package/browser/payments/p2ms.d.ts +150 -0
- package/browser/payments/p2ms.d.ts.map +1 -0
- package/browser/payments/p2op.d.ts +150 -24
- package/browser/payments/p2op.d.ts.map +1 -0
- package/browser/payments/p2pk.d.ts +154 -1
- package/browser/payments/p2pk.d.ts.map +1 -0
- package/browser/payments/p2pkh.d.ts +176 -1
- package/browser/payments/p2pkh.d.ts.map +1 -0
- package/browser/payments/p2sh.d.ts +150 -1
- package/browser/payments/p2sh.d.ts.map +1 -0
- package/browser/payments/p2tr.d.ts +185 -1
- package/browser/payments/p2tr.d.ts.map +1 -0
- package/browser/payments/p2wpkh.d.ts +161 -1
- package/browser/payments/p2wpkh.d.ts.map +1 -0
- package/browser/payments/p2wsh.d.ts +146 -1
- package/browser/payments/p2wsh.d.ts.map +1 -0
- package/browser/payments/types.d.ts +94 -64
- package/browser/payments/types.d.ts.map +1 -0
- package/browser/psbt/bip371.d.ts +34 -8
- package/browser/psbt/bip371.d.ts.map +1 -0
- package/browser/psbt/psbtutils.d.ts +56 -16
- package/browser/psbt/psbtutils.d.ts.map +1 -0
- package/browser/psbt/types.d.ts +245 -0
- package/browser/psbt/types.d.ts.map +1 -0
- package/browser/psbt/utils.d.ts +64 -0
- package/browser/psbt/utils.d.ts.map +1 -0
- package/browser/psbt/validation.d.ts +84 -0
- package/browser/psbt/validation.d.ts.map +1 -0
- package/browser/psbt.d.ts +82 -118
- package/browser/psbt.d.ts.map +1 -0
- package/browser/pubkey.d.ts +27 -6
- package/browser/pubkey.d.ts.map +1 -0
- package/browser/push_data.d.ts +24 -2
- package/browser/push_data.d.ts.map +1 -0
- package/browser/script.d.ts +33 -8
- package/browser/script.d.ts.map +1 -0
- package/browser/script_number.d.ts +17 -0
- package/browser/script_number.d.ts.map +1 -0
- package/browser/script_signature.d.ts +23 -5
- package/browser/script_signature.d.ts.map +1 -0
- package/browser/transaction.d.ts +160 -18
- package/browser/transaction.d.ts.map +1 -0
- package/browser/types.d.ts +36 -38
- package/browser/types.d.ts.map +1 -0
- package/browser/workers/WorkerSigningPool.d.ts +143 -0
- package/browser/workers/WorkerSigningPool.d.ts.map +1 -0
- package/browser/workers/WorkerSigningPool.node.d.ts +116 -0
- package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -0
- package/browser/workers/ecc-bundle.d.ts +25 -0
- package/browser/workers/ecc-bundle.d.ts.map +1 -0
- package/browser/workers/index.d.ts +91 -0
- package/browser/workers/index.d.ts.map +1 -0
- package/browser/workers/psbt-parallel.d.ts +88 -0
- package/browser/workers/psbt-parallel.d.ts.map +1 -0
- package/browser/workers/signing-worker.d.ts +37 -0
- package/browser/workers/signing-worker.d.ts.map +1 -0
- package/browser/workers/types.d.ts +365 -0
- package/browser/workers/types.d.ts.map +1 -0
- package/build/address.d.ts +58 -11
- package/build/address.d.ts.map +1 -0
- package/build/address.js +82 -25
- package/build/address.js.map +1 -0
- package/build/bech32utils.d.ts +9 -1
- package/build/bech32utils.d.ts.map +1 -0
- package/build/bech32utils.js +10 -2
- package/build/bech32utils.js.map +1 -0
- package/build/bip66.d.ts +11 -6
- package/build/bip66.d.ts.map +1 -0
- package/build/bip66.js +32 -3
- package/build/bip66.js.map +1 -0
- package/build/block.d.ts +117 -11
- package/build/block.d.ts.map +1 -0
- package/build/block.js +202 -72
- package/build/block.js.map +1 -0
- package/build/branded.d.ts +20 -0
- package/build/branded.d.ts.map +1 -0
- package/build/branded.js +7 -0
- package/build/branded.js.map +1 -0
- package/build/crypto/crypto.d.ts +1 -0
- package/build/crypto/crypto.d.ts.map +1 -0
- package/build/crypto/crypto.js +1 -0
- package/build/crypto/crypto.js.map +1 -0
- package/build/crypto.d.ts +46 -7
- package/build/crypto.d.ts.map +1 -0
- package/build/crypto.js +65 -20
- package/build/crypto.js.map +1 -0
- package/build/ecc/context.d.ts +135 -0
- package/build/ecc/context.d.ts.map +1 -0
- package/build/ecc/context.js +232 -0
- package/build/ecc/context.js.map +1 -0
- package/build/ecc/index.d.ts +11 -0
- package/build/ecc/index.d.ts.map +1 -0
- package/build/ecc/index.js +11 -0
- package/build/ecc/index.js.map +1 -0
- package/build/ecc/types.d.ts +134 -0
- package/build/ecc/types.d.ts.map +1 -0
- package/build/ecc/types.js +8 -0
- package/build/ecc/types.js.map +1 -0
- package/build/errors.d.ts +124 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +155 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +32 -5
- package/build/index.d.ts.map +1 -0
- package/build/index.js +26 -3
- package/build/index.js.map +1 -0
- package/build/io/BinaryReader.d.ts +276 -0
- package/build/io/BinaryReader.d.ts.map +1 -0
- package/build/io/BinaryReader.js +425 -0
- package/build/io/BinaryReader.js.map +1 -0
- package/build/io/BinaryWriter.d.ts +391 -0
- package/build/io/BinaryWriter.d.ts.map +1 -0
- package/build/io/BinaryWriter.js +611 -0
- package/build/io/BinaryWriter.js.map +1 -0
- package/build/io/MemoryPool.d.ts +220 -0
- package/build/io/MemoryPool.d.ts.map +1 -0
- package/build/io/MemoryPool.js +309 -0
- package/build/io/MemoryPool.js.map +1 -0
- package/build/io/base64.d.ts +13 -0
- package/build/io/base64.d.ts.map +1 -0
- package/build/io/base64.js +20 -0
- package/build/io/base64.js.map +1 -0
- package/build/io/hex.d.ts +67 -0
- package/build/io/hex.d.ts.map +1 -0
- package/build/io/hex.js +138 -0
- package/build/io/hex.js.map +1 -0
- package/build/io/index.d.ts +17 -0
- package/build/io/index.d.ts.map +1 -0
- package/build/io/index.js +23 -0
- package/build/io/index.js.map +1 -0
- package/build/io/utils.d.ts +199 -0
- package/build/io/utils.d.ts.map +1 -0
- package/build/io/utils.js +271 -0
- package/build/io/utils.js.map +1 -0
- package/build/merkle.d.ts +10 -1
- package/build/merkle.d.ts.map +1 -0
- package/build/merkle.js +12 -1
- package/build/merkle.js.map +1 -0
- package/build/networks.d.ts +70 -9
- package/build/networks.d.ts.map +1 -0
- package/build/networks.js +90 -4
- package/build/networks.js.map +1 -0
- package/build/opcodes.d.ts +1 -0
- package/build/opcodes.d.ts.map +1 -0
- package/build/opcodes.js +1 -0
- package/build/opcodes.js.map +1 -0
- package/build/payments/bip341.d.ts +35 -9
- package/build/payments/bip341.d.ts.map +1 -0
- package/build/payments/bip341.js +34 -15
- package/build/payments/bip341.js.map +1 -0
- package/build/payments/embed.d.ts +120 -1
- package/build/payments/embed.d.ts.map +1 -0
- package/build/payments/embed.js +215 -34
- package/build/payments/embed.js.map +1 -0
- package/build/payments/index.d.ts +17 -10
- package/build/payments/index.d.ts.map +1 -0
- package/build/payments/index.js +20 -10
- package/build/payments/index.js.map +1 -0
- package/build/payments/p2ms.d.ts +159 -1
- package/build/payments/p2ms.d.ts.map +1 -0
- package/build/payments/p2ms.js +427 -108
- package/build/payments/p2ms.js.map +1 -0
- package/build/payments/p2op.d.ts +158 -24
- package/build/payments/p2op.d.ts.map +1 -0
- package/build/payments/p2op.js +379 -93
- package/build/payments/p2op.js.map +1 -0
- package/build/payments/p2pk.d.ts +162 -1
- package/build/payments/p2pk.d.ts.map +1 -0
- package/build/payments/p2pk.js +327 -58
- package/build/payments/p2pk.js.map +1 -0
- package/build/payments/p2pkh.d.ts +185 -1
- package/build/payments/p2pkh.d.ts.map +1 -0
- package/build/payments/p2pkh.js +467 -114
- package/build/payments/p2pkh.js.map +1 -0
- package/build/payments/p2sh.d.ts +159 -1
- package/build/payments/p2sh.d.ts.map +1 -0
- package/build/payments/p2sh.js +500 -150
- package/build/payments/p2sh.js.map +1 -0
- package/build/payments/p2tr.d.ts +193 -1
- package/build/payments/p2tr.d.ts.map +1 -0
- package/build/payments/p2tr.js +592 -174
- package/build/payments/p2tr.js.map +1 -0
- package/build/payments/p2wpkh.d.ts +170 -1
- package/build/payments/p2wpkh.d.ts.map +1 -0
- package/build/payments/p2wpkh.js +428 -103
- package/build/payments/p2wpkh.js.map +1 -0
- package/build/payments/p2wsh.d.ts +155 -1
- package/build/payments/p2wsh.d.ts.map +1 -0
- package/build/payments/p2wsh.js +465 -143
- package/build/payments/p2wsh.js.map +1 -0
- package/build/payments/types.d.ts +98 -64
- package/build/payments/types.d.ts.map +1 -0
- package/build/payments/types.js +17 -13
- package/build/payments/types.js.map +1 -0
- package/build/psbt/bip371.d.ts +35 -9
- package/build/psbt/bip371.d.ts.map +1 -0
- package/build/psbt/bip371.js +117 -28
- package/build/psbt/bip371.js.map +1 -0
- package/build/psbt/psbtutils.d.ts +56 -16
- package/build/psbt/psbtutils.d.ts.map +1 -0
- package/build/psbt/psbtutils.js +71 -16
- package/build/psbt/psbtutils.js.map +1 -0
- package/build/psbt/types.d.ts +249 -0
- package/build/psbt/types.d.ts.map +1 -0
- package/build/psbt/types.js +6 -0
- package/build/psbt/types.js.map +1 -0
- package/build/psbt/utils.d.ts +68 -0
- package/build/psbt/utils.d.ts.map +1 -0
- package/build/psbt/utils.js +171 -0
- package/build/psbt/utils.js.map +1 -0
- package/build/psbt/validation.d.ts +88 -0
- package/build/psbt/validation.d.ts.map +1 -0
- package/build/psbt/validation.js +149 -0
- package/build/psbt/validation.js.map +1 -0
- package/build/psbt.d.ts +84 -120
- package/build/psbt.d.ts.map +1 -0
- package/build/psbt.js +406 -413
- package/build/psbt.js.map +1 -0
- package/build/pubkey.d.ts +27 -6
- package/build/pubkey.d.ts.map +1 -0
- package/build/pubkey.js +36 -12
- package/build/pubkey.js.map +1 -0
- package/build/push_data.d.ts +24 -2
- package/build/push_data.d.ts.map +1 -0
- package/build/push_data.js +44 -12
- package/build/push_data.js.map +1 -0
- package/build/script.d.ts +33 -8
- package/build/script.d.ts.map +1 -0
- package/build/script.js +101 -37
- package/build/script.js.map +1 -0
- package/build/script_number.d.ts +17 -0
- package/build/script_number.d.ts.map +1 -0
- package/build/script_number.js +19 -0
- package/build/script_number.js.map +1 -0
- package/build/script_signature.d.ts +23 -5
- package/build/script_signature.d.ts.map +1 -0
- package/build/script_signature.js +48 -15
- package/build/script_signature.js.map +1 -0
- package/build/transaction.d.ts +160 -18
- package/build/transaction.d.ts.map +1 -0
- package/build/transaction.js +443 -176
- package/build/transaction.js.map +1 -0
- package/build/tsconfig.build.tsbuildinfo +1 -0
- package/build/types.d.ts +36 -38
- package/build/types.d.ts.map +1 -0
- package/build/types.js +169 -57
- package/build/types.js.map +1 -0
- package/build/workers/WorkerSigningPool.d.ts +174 -0
- package/build/workers/WorkerSigningPool.d.ts.map +1 -0
- package/build/workers/WorkerSigningPool.js +553 -0
- package/build/workers/WorkerSigningPool.js.map +1 -0
- package/build/workers/WorkerSigningPool.node.d.ts +124 -0
- package/build/workers/WorkerSigningPool.node.d.ts.map +1 -0
- package/build/workers/WorkerSigningPool.node.js +753 -0
- package/build/workers/WorkerSigningPool.node.js.map +1 -0
- package/build/workers/ecc-bundle.d.ts +25 -0
- package/build/workers/ecc-bundle.d.ts.map +1 -0
- package/build/workers/ecc-bundle.js +25 -0
- package/build/workers/ecc-bundle.js.map +1 -0
- package/build/workers/index.d.ts +91 -0
- package/build/workers/index.d.ts.map +1 -0
- package/build/workers/index.js +114 -0
- package/build/workers/index.js.map +1 -0
- package/build/workers/psbt-parallel.d.ts +117 -0
- package/build/workers/psbt-parallel.d.ts.map +1 -0
- package/build/workers/psbt-parallel.js +233 -0
- package/build/workers/psbt-parallel.js.map +1 -0
- package/build/workers/signing-worker.d.ts +37 -0
- package/build/workers/signing-worker.d.ts.map +1 -0
- package/build/workers/signing-worker.js +350 -0
- package/build/workers/signing-worker.js.map +1 -0
- package/build/workers/types.d.ts +365 -0
- package/build/workers/types.d.ts.map +1 -0
- package/build/workers/types.js +60 -0
- package/build/workers/types.js.map +1 -0
- package/package.json +68 -9
- package/scripts/bundle-ecc.ts +111 -0
- package/src/address.ts +91 -45
- package/src/bech32utils.ts +3 -3
- package/src/bip66.ts +34 -24
- package/src/block.ts +205 -86
- package/src/branded.ts +18 -0
- package/src/crypto.ts +64 -26
- package/src/ecc/context.ts +280 -0
- package/src/ecc/index.ts +14 -0
- package/src/ecc/types.ts +147 -0
- package/src/ecpair.d.ts +99 -0
- package/src/errors.ts +163 -0
- package/src/index.ts +112 -9
- package/src/io/BinaryReader.ts +461 -0
- package/src/io/BinaryWriter.ts +696 -0
- package/src/io/MemoryPool.ts +343 -0
- package/src/io/base64.ts +20 -0
- package/src/io/hex.ts +155 -0
- package/src/io/index.ts +41 -0
- package/src/io/utils.ts +283 -0
- package/src/merkle.ts +14 -9
- package/src/networks.ts +9 -9
- package/src/payments/bip341.ts +32 -33
- package/src/payments/embed.ts +244 -41
- package/src/payments/index.ts +12 -10
- package/src/payments/p2ms.ts +497 -118
- package/src/payments/p2op.ts +432 -134
- package/src/payments/p2pk.ts +370 -72
- package/src/payments/p2pkh.ts +524 -130
- package/src/payments/p2sh.ts +572 -169
- package/src/payments/p2tr.ts +686 -194
- package/src/payments/p2wpkh.ts +482 -105
- package/src/payments/p2wsh.ts +524 -162
- package/src/payments/types.ts +80 -66
- package/src/psbt/bip371.ts +72 -51
- package/src/psbt/psbtutils.ts +39 -40
- package/src/psbt/types.ts +324 -0
- package/src/psbt/utils.ts +188 -0
- package/src/psbt/validation.ts +185 -0
- package/src/psbt.ts +608 -827
- package/src/pubkey.ts +22 -23
- package/src/push_data.ts +18 -16
- package/src/script.ts +81 -66
- package/src/script_number.ts +6 -6
- package/src/script_signature.ts +33 -36
- package/src/transaction.ts +462 -239
- package/src/types.ts +229 -100
- package/src/workers/WorkerSigningPool.node.ts +887 -0
- package/src/workers/WorkerSigningPool.ts +666 -0
- package/src/workers/ecc-bundle.ts +26 -0
- package/src/workers/index.ts +165 -0
- package/src/workers/psbt-parallel.ts +327 -0
- package/src/workers/signing-worker.ts +353 -0
- package/src/workers/types.ts +417 -0
- package/test/address.spec.ts +9 -6
- package/test/bitcoin.core.spec.ts +16 -17
- package/test/block.spec.ts +8 -7
- package/test/bufferutils.spec.ts +228 -214
- package/test/crypto.spec.ts +19 -11
- package/test/fixtures/p2pk.json +0 -8
- package/test/fixtures/p2pkh.json +1 -1
- package/test/fixtures/p2sh.json +1 -1
- package/test/fixtures/script.json +1 -1
- package/test/fixtures/transaction.json +2 -2
- package/test/integration/_regtest.ts +25 -0
- package/test/integration/addresses.spec.ts +4 -3
- package/test/integration/bip32.spec.ts +2 -1
- package/test/integration/blocks.spec.ts +1 -1
- package/test/integration/cltv.spec.ts +18 -16
- package/test/integration/csv.spec.ts +37 -64
- package/test/integration/payments.spec.ts +5 -3
- package/test/integration/taproot.spec.ts +76 -83
- package/test/integration/transactions.spec.ts +38 -35
- package/test/payments.spec.ts +35 -13
- package/test/payments.utils.ts +17 -16
- package/test/psbt.spec.ts +111 -100
- package/test/script.spec.ts +11 -10
- package/test/script_signature.spec.ts +9 -11
- package/test/taproot-cache.spec.ts +694 -0
- package/test/transaction.spec.ts +32 -40
- package/test/types.spec.ts +74 -29
- package/test/workers-pool.spec.ts +963 -0
- package/test/workers-signing.spec.ts +635 -0
- package/test/workers.spec.ts +1390 -0
- package/tsconfig.base.json +34 -18
- package/tsconfig.browser.json +15 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +5 -14
- package/typedoc.json +29 -0
- package/vite.config.browser.ts +3 -42
- package/vitest.config.integration.ts +2 -0
- package/browser/bufferutils.d.ts +0 -34
- package/browser/chunks/crypto-BhCpKpek.js +0 -2033
- package/browser/chunks/payments-B1wlSccx.js +0 -1089
- package/browser/chunks/psbt-BCNk7JUx.js +0 -4055
- package/browser/chunks/script-DyPItFEl.js +0 -318
- package/browser/chunks/transaction-C_UbhMGn.js +0 -432
- package/browser/chunks/utils-DNZi-T5W.js +0 -761
- package/browser/ecc_lib.d.ts +0 -3
- package/browser/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/browser/hooks/HookedSigner.d.ts +0 -4
- package/browser/hooks/SignatureManager.d.ts +0 -13
- package/browser/payments/lazy.d.ts +0 -2
- package/browser/typeforce.d.ts +0 -38
- package/build/bufferutils.d.ts +0 -34
- package/build/bufferutils.js +0 -141
- package/build/ecc_lib.d.ts +0 -3
- package/build/ecc_lib.js +0 -61
- package/build/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/build/hooks/AdvancedSignatureManager.js +0 -52
- package/build/hooks/HookedSigner.d.ts +0 -4
- package/build/hooks/HookedSigner.js +0 -64
- package/build/hooks/SignatureManager.d.ts +0 -13
- package/build/hooks/SignatureManager.js +0 -45
- package/build/payments/lazy.d.ts +0 -2
- package/build/payments/lazy.js +0 -28
- package/build/tsconfig.tsbuildinfo +0 -1
- package/src/bufferutils.ts +0 -188
- package/src/ecc_lib.ts +0 -94
- package/src/hooks/AdvancedSignatureManager.ts +0 -104
- package/src/hooks/HookedSigner.ts +0 -108
- package/src/hooks/SignatureManager.ts +0 -84
- package/src/payments/lazy.ts +0 -28
- package/src/typeforce.d.ts +0 -38
- package/tsconfig.webpack.json +0 -18
package/src/psbt.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as varuint from 'bip174/src/lib/converter/varint.js';
|
|
3
|
-
|
|
4
|
-
interface VaruintDecode {
|
|
5
|
-
(buffer: Buffer, offset?: number): number;
|
|
6
|
-
bytes: number;
|
|
7
|
-
}
|
|
8
|
-
const varuintDecode = varuint.decode as VaruintDecode;
|
|
9
|
-
import {
|
|
1
|
+
import type {
|
|
10
2
|
Bip32Derivation,
|
|
11
3
|
KeyValue,
|
|
12
4
|
PartialSig,
|
|
13
|
-
PsbtGlobal,
|
|
14
5
|
PsbtGlobalUpdate,
|
|
15
6
|
PsbtInput,
|
|
16
7
|
PsbtInputUpdate,
|
|
@@ -20,62 +11,116 @@ import {
|
|
|
20
11
|
TapScriptSig,
|
|
21
12
|
Transaction as ITransaction,
|
|
22
13
|
TransactionFromBuffer,
|
|
23
|
-
} from 'bip174
|
|
24
|
-
import { checkForInput, checkForOutput } from 'bip174
|
|
25
|
-
import {
|
|
26
|
-
|
|
14
|
+
} from 'bip174';
|
|
15
|
+
import { checkForInput, checkForOutput, Psbt as PsbtBase } from 'bip174';
|
|
16
|
+
import { clone, equals, fromBase64, fromHex, reverse, toHex } from './io/index.js';
|
|
17
|
+
|
|
18
|
+
import type { BIP32Interface } from '@btc-vision/bip32';
|
|
19
|
+
import type { ECPairInterface } from 'ecpair';
|
|
27
20
|
import { fromOutputScript, isUnknownSegwitVersion, toOutputScript } from './address.js';
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
21
|
+
import { bitcoin as btcNetwork } from './networks.js';
|
|
22
|
+
import type { P2SHPayment, P2WSHPayment } from './payments/index.js';
|
|
30
23
|
import * as payments from './payments/index.js';
|
|
31
|
-
import type { P2WSHPayment } from './payments/index.js';
|
|
32
24
|
import { tapleafHash } from './payments/bip341.js';
|
|
33
|
-
import { P2SHPayment, Payment, PaymentOpts } from './payments/index.js';
|
|
34
25
|
import {
|
|
35
26
|
checkTaprootInputFields,
|
|
36
|
-
checkTaprootInputForSigs,
|
|
37
27
|
checkTaprootOutputFields,
|
|
38
28
|
isTaprootInput,
|
|
39
29
|
serializeTaprootSignature,
|
|
40
30
|
tapScriptFinalizer,
|
|
41
31
|
} from './psbt/bip371.js';
|
|
42
32
|
import { toXOnly } from './pubkey.js';
|
|
33
|
+
import { isP2TR, isP2WPKH, pubkeyInScript, witnessStackToScriptWitness } from './psbt/psbtutils.js';
|
|
43
34
|
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
35
|
+
check32Bit,
|
|
36
|
+
checkCache,
|
|
37
|
+
checkInputsForPartialSig,
|
|
38
|
+
checkPartialSigSighashes,
|
|
39
|
+
checkScriptForPubkey,
|
|
40
|
+
checkTxEmpty,
|
|
41
|
+
checkTxForDupeIns,
|
|
42
|
+
checkTxInputCache,
|
|
43
|
+
isFinalized,
|
|
44
|
+
} from './psbt/validation.js';
|
|
45
|
+
import {
|
|
46
|
+
checkInvalidP2WSH,
|
|
47
|
+
classifyScript,
|
|
48
|
+
compressPubkey,
|
|
49
|
+
getMeaningfulScript,
|
|
50
|
+
isPubkeyLike,
|
|
51
|
+
isSigLike,
|
|
52
|
+
range,
|
|
53
|
+
scriptWitnessToWitnessStack,
|
|
54
|
+
sighashTypeToString,
|
|
55
|
+
} from './psbt/utils.js';
|
|
55
56
|
import * as bscript from './script.js';
|
|
56
|
-
import { Output
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
57
|
+
import type { Output } from './transaction.js';
|
|
58
|
+
import { Transaction } from './transaction.js';
|
|
59
|
+
import type {
|
|
60
|
+
Bytes20,
|
|
61
|
+
Bytes32,
|
|
62
|
+
PublicKey,
|
|
63
|
+
Satoshi,
|
|
64
|
+
SchnorrSignature,
|
|
65
|
+
Script,
|
|
66
|
+
Signature,
|
|
67
|
+
XOnlyPublicKey,
|
|
68
|
+
} from './types.js';
|
|
69
|
+
// Import types for internal use
|
|
70
|
+
import type {
|
|
71
|
+
AllScriptType,
|
|
72
|
+
FinalScriptsFunc,
|
|
73
|
+
FinalTaprootScriptsFunc,
|
|
74
|
+
GetScriptReturn,
|
|
75
|
+
HDSigner,
|
|
76
|
+
HDSignerAsync,
|
|
77
|
+
PsbtBaseExtended,
|
|
78
|
+
PsbtCache,
|
|
79
|
+
PsbtInputExtended,
|
|
80
|
+
PsbtOpts,
|
|
81
|
+
PsbtOptsOptional,
|
|
82
|
+
PsbtOutputExtended,
|
|
83
|
+
PsbtOutputExtendedAddress,
|
|
84
|
+
PsbtTxInput,
|
|
85
|
+
PsbtTxOutput,
|
|
86
|
+
Signer,
|
|
87
|
+
SignerAlternative,
|
|
88
|
+
SignerAsync,
|
|
89
|
+
TaprootHashCheckSigner,
|
|
90
|
+
TransactionInput,
|
|
91
|
+
TransactionOutput,
|
|
92
|
+
TxCacheNumberKey,
|
|
93
|
+
ValidateSigFunction,
|
|
94
|
+
} from './psbt/types.js';
|
|
95
|
+
|
|
96
|
+
// Re-export types from the types module
|
|
97
|
+
export type {
|
|
98
|
+
TransactionInput,
|
|
99
|
+
PsbtTxInput,
|
|
100
|
+
TransactionOutput,
|
|
101
|
+
PsbtTxOutput,
|
|
102
|
+
ValidateSigFunction,
|
|
103
|
+
PsbtBaseExtended,
|
|
104
|
+
PsbtOptsOptional,
|
|
105
|
+
PsbtOpts,
|
|
106
|
+
PsbtInputExtended,
|
|
107
|
+
PsbtOutputExtended,
|
|
108
|
+
PsbtOutputExtendedAddress,
|
|
109
|
+
PsbtOutputExtendedScript,
|
|
110
|
+
HDSigner,
|
|
111
|
+
HDSignerAsync,
|
|
112
|
+
SignerAlternative,
|
|
113
|
+
Signer,
|
|
114
|
+
SignerAsync,
|
|
115
|
+
TaprootHashCheckSigner,
|
|
116
|
+
PsbtCache,
|
|
117
|
+
TxCacheNumberKey,
|
|
118
|
+
ScriptType,
|
|
119
|
+
AllScriptType,
|
|
120
|
+
GetScriptReturn,
|
|
121
|
+
FinalScriptsFunc,
|
|
122
|
+
FinalTaprootScriptsFunc,
|
|
123
|
+
} from './psbt/types.js';
|
|
79
124
|
|
|
80
125
|
/**
|
|
81
126
|
* These are the default arguments for a Psbt instance.
|
|
@@ -94,12 +139,6 @@ const DEFAULT_OPTS: PsbtOpts = {
|
|
|
94
139
|
maximumFeeRate: 5000, // satoshi per byte
|
|
95
140
|
};
|
|
96
141
|
|
|
97
|
-
// Not a breaking change.
|
|
98
|
-
export interface PsbtBaseExtended extends Omit<PsbtBase, 'inputs'> {
|
|
99
|
-
inputs: PsbtInput[];
|
|
100
|
-
globalMap: PsbtGlobal;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
142
|
/**
|
|
104
143
|
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
105
144
|
* There are 6 roles that this class fulfills. (Explained in BIP174)
|
|
@@ -110,9 +149,9 @@ export interface PsbtBaseExtended extends Omit<PsbtBase, 'inputs'> {
|
|
|
110
149
|
* `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
|
|
111
150
|
* add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
|
|
112
151
|
* `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
|
|
113
|
-
* addInput requires hash:
|
|
152
|
+
* addInput requires hash: Uint8Array | string; and index: number; as attributes
|
|
114
153
|
* and can also include any attributes that are used in updateInput method.
|
|
115
|
-
* addOutput requires script:
|
|
154
|
+
* addOutput requires script: Uint8Array; and value: bigint; and likewise can include
|
|
116
155
|
* data for updateOutput.
|
|
117
156
|
* For a list of what attributes should be what types. Check the bip174 library.
|
|
118
157
|
* Also, check the integration tests for some examples of usage.
|
|
@@ -141,36 +180,37 @@ export interface PsbtBaseExtended extends Omit<PsbtBase, 'inputs'> {
|
|
|
141
180
|
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
142
181
|
*/
|
|
143
182
|
export class Psbt {
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
readonly #cache: PsbtCache;
|
|
184
|
+
readonly #opts: PsbtOpts;
|
|
146
185
|
|
|
147
186
|
constructor(
|
|
148
187
|
opts: PsbtOptsOptional = {},
|
|
149
188
|
public data: PsbtBaseExtended = new PsbtBase(new PsbtTransaction()),
|
|
150
189
|
) {
|
|
151
|
-
this
|
|
152
|
-
this
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
190
|
+
this.#opts = Object.assign({}, DEFAULT_OPTS, opts);
|
|
191
|
+
this.#cache = {
|
|
192
|
+
nonWitnessUtxoTxCache: [],
|
|
193
|
+
nonWitnessUtxoBufCache: [],
|
|
194
|
+
txInCache: {},
|
|
156
195
|
// unsignedTx.tx property is dynamically added by PsbtBase
|
|
157
|
-
|
|
158
|
-
|
|
196
|
+
tx: (this.data.globalMap.unsignedTx as PsbtTransaction).tx,
|
|
197
|
+
unsafeSignNonSegwit: false,
|
|
198
|
+
hasSignatures: false,
|
|
159
199
|
};
|
|
160
200
|
|
|
161
201
|
if (opts.version === 3) {
|
|
162
202
|
this.setVersionTRUC();
|
|
163
203
|
} else if (this.data.inputs.length === 0) this.setVersion(2);
|
|
204
|
+
}
|
|
164
205
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
170
|
-
};
|
|
206
|
+
/** @internal - Exposed for testing. Do not use in production code. */
|
|
207
|
+
get __CACHE(): PsbtCache {
|
|
208
|
+
return this.#cache;
|
|
209
|
+
}
|
|
171
210
|
|
|
172
|
-
|
|
173
|
-
|
|
211
|
+
/** @internal - Exposed for testing. Do not use in production code. */
|
|
212
|
+
get opts(): PsbtOpts {
|
|
213
|
+
return this.#opts;
|
|
174
214
|
}
|
|
175
215
|
|
|
176
216
|
get inputCount(): number {
|
|
@@ -178,7 +218,7 @@ export class Psbt {
|
|
|
178
218
|
}
|
|
179
219
|
|
|
180
220
|
get version(): number {
|
|
181
|
-
return this.
|
|
221
|
+
return this.#cache.tx.version;
|
|
182
222
|
}
|
|
183
223
|
|
|
184
224
|
set version(version: number) {
|
|
@@ -186,7 +226,7 @@ export class Psbt {
|
|
|
186
226
|
}
|
|
187
227
|
|
|
188
228
|
get locktime(): number {
|
|
189
|
-
return this.
|
|
229
|
+
return this.#cache.tx.locktime;
|
|
190
230
|
}
|
|
191
231
|
|
|
192
232
|
set locktime(locktime: number) {
|
|
@@ -194,21 +234,21 @@ export class Psbt {
|
|
|
194
234
|
}
|
|
195
235
|
|
|
196
236
|
get txInputs(): PsbtTxInput[] {
|
|
197
|
-
return this.
|
|
198
|
-
hash:
|
|
237
|
+
return this.#cache.tx.ins.map((input) => ({
|
|
238
|
+
hash: clone(input.hash) as Bytes32,
|
|
199
239
|
index: input.index,
|
|
200
240
|
sequence: input.sequence,
|
|
201
241
|
}));
|
|
202
242
|
}
|
|
203
243
|
|
|
204
244
|
get txOutputs(): PsbtTxOutput[] {
|
|
205
|
-
return this.
|
|
245
|
+
return this.#cache.tx.outs.map((output) => {
|
|
206
246
|
let address;
|
|
207
247
|
try {
|
|
208
|
-
address = fromOutputScript(output.script, this
|
|
248
|
+
address = fromOutputScript(output.script, this.#opts.network);
|
|
209
249
|
} catch (_) {}
|
|
210
250
|
return {
|
|
211
|
-
script:
|
|
251
|
+
script: clone(output.script) as Script,
|
|
212
252
|
value: output.value,
|
|
213
253
|
address,
|
|
214
254
|
};
|
|
@@ -216,19 +256,28 @@ export class Psbt {
|
|
|
216
256
|
}
|
|
217
257
|
|
|
218
258
|
static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
219
|
-
const buffer =
|
|
259
|
+
const buffer = fromBase64(data);
|
|
220
260
|
return this.fromBuffer(buffer, opts);
|
|
221
261
|
}
|
|
222
262
|
|
|
223
263
|
static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
|
|
224
|
-
const buffer =
|
|
264
|
+
const buffer = fromHex(data);
|
|
225
265
|
return this.fromBuffer(buffer, opts);
|
|
226
266
|
}
|
|
227
267
|
|
|
228
|
-
static fromBuffer(buffer:
|
|
268
|
+
static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
|
|
229
269
|
const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
|
|
230
270
|
const psbt = new Psbt(opts, psbtBase);
|
|
231
|
-
checkTxForDupeIns(psbt.
|
|
271
|
+
checkTxForDupeIns(psbt.#cache.tx, psbt.#cache);
|
|
272
|
+
// Check if restored PSBT has any signatures (partial or finalized)
|
|
273
|
+
psbt.#cache.hasSignatures = psbt.data.inputs.some(
|
|
274
|
+
(input) =>
|
|
275
|
+
input.partialSig?.length ||
|
|
276
|
+
input.tapKeySig ||
|
|
277
|
+
input.tapScriptSig?.length ||
|
|
278
|
+
input.finalScriptSig ||
|
|
279
|
+
input.finalScriptWitness,
|
|
280
|
+
);
|
|
232
281
|
return psbt;
|
|
233
282
|
}
|
|
234
283
|
|
|
@@ -239,21 +288,21 @@ export class Psbt {
|
|
|
239
288
|
|
|
240
289
|
clone(): Psbt {
|
|
241
290
|
// TODO: more efficient cloning
|
|
242
|
-
const clonedOpts = JSON.parse(JSON.stringify(this
|
|
243
|
-
return Psbt.fromBuffer(this.data.toBuffer(), clonedOpts);
|
|
291
|
+
const clonedOpts = JSON.parse(JSON.stringify(this.#opts)) as PsbtOptsOptional;
|
|
292
|
+
return Psbt.fromBuffer(new Uint8Array(this.data.toBuffer()), clonedOpts);
|
|
244
293
|
}
|
|
245
294
|
|
|
246
295
|
setMaximumFeeRate(satoshiPerByte: number): void {
|
|
247
296
|
check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
|
|
248
|
-
this
|
|
297
|
+
this.#opts.maximumFeeRate = satoshiPerByte;
|
|
249
298
|
}
|
|
250
299
|
|
|
251
300
|
setVersion(version: number): this {
|
|
252
301
|
check32Bit(version);
|
|
253
|
-
checkInputsForPartialSig(this.data.inputs, 'setVersion');
|
|
254
|
-
const c = this
|
|
255
|
-
c.
|
|
256
|
-
c.
|
|
302
|
+
checkInputsForPartialSig(this.data.inputs, 'setVersion', this.#cache.hasSignatures);
|
|
303
|
+
const c = this.#cache;
|
|
304
|
+
c.tx.version = version;
|
|
305
|
+
c.extractedTx = undefined;
|
|
257
306
|
return this;
|
|
258
307
|
}
|
|
259
308
|
|
|
@@ -263,22 +312,22 @@ export class Psbt {
|
|
|
263
312
|
|
|
264
313
|
setLocktime(locktime: number): this {
|
|
265
314
|
check32Bit(locktime);
|
|
266
|
-
checkInputsForPartialSig(this.data.inputs, 'setLocktime');
|
|
267
|
-
const c = this
|
|
268
|
-
c.
|
|
269
|
-
c.
|
|
315
|
+
checkInputsForPartialSig(this.data.inputs, 'setLocktime', this.#cache.hasSignatures);
|
|
316
|
+
const c = this.#cache;
|
|
317
|
+
c.tx.locktime = locktime;
|
|
318
|
+
c.extractedTx = undefined;
|
|
270
319
|
return this;
|
|
271
320
|
}
|
|
272
321
|
|
|
273
322
|
setInputSequence(inputIndex: number, sequence: number): this {
|
|
274
323
|
check32Bit(sequence);
|
|
275
|
-
checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
|
|
276
|
-
const c = this
|
|
277
|
-
if (c.
|
|
324
|
+
checkInputsForPartialSig(this.data.inputs, 'setInputSequence', this.#cache.hasSignatures);
|
|
325
|
+
const c = this.#cache;
|
|
326
|
+
if (c.tx.ins.length <= inputIndex) {
|
|
278
327
|
throw new Error('Input index too high');
|
|
279
328
|
}
|
|
280
|
-
c.
|
|
281
|
-
c.
|
|
329
|
+
c.tx.ins[inputIndex]!.sequence = sequence;
|
|
330
|
+
c.extractedTx = undefined;
|
|
282
331
|
return this;
|
|
283
332
|
}
|
|
284
333
|
|
|
@@ -298,90 +347,118 @@ export class Psbt {
|
|
|
298
347
|
checkTaprootInputFields(inputData, inputData, 'addInput');
|
|
299
348
|
|
|
300
349
|
if (checkPartialSigs) {
|
|
301
|
-
checkInputsForPartialSig(this.data.inputs, 'addInput');
|
|
350
|
+
checkInputsForPartialSig(this.data.inputs, 'addInput', this.#cache.hasSignatures);
|
|
302
351
|
}
|
|
303
352
|
|
|
304
353
|
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
354
|
+
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
355
|
+
const normalizedInputData = inputData.witnessUtxo
|
|
356
|
+
? {
|
|
357
|
+
...inputData,
|
|
358
|
+
witnessUtxo: {
|
|
359
|
+
script: inputData.witnessUtxo.script,
|
|
360
|
+
value:
|
|
361
|
+
typeof inputData.witnessUtxo.value === 'bigint'
|
|
362
|
+
? inputData.witnessUtxo.value
|
|
363
|
+
: BigInt(inputData.witnessUtxo.value),
|
|
364
|
+
},
|
|
365
|
+
}
|
|
366
|
+
: inputData;
|
|
367
|
+
const c = this.#cache;
|
|
368
|
+
this.data.addInput(normalizedInputData);
|
|
369
|
+
const txIn = c.tx.ins[c.tx.ins.length - 1]!;
|
|
308
370
|
checkTxInputCache(c, txIn);
|
|
309
371
|
|
|
310
372
|
const inputIndex = this.data.inputs.length - 1;
|
|
311
|
-
const input = this.data.inputs[inputIndex]
|
|
373
|
+
const input = this.data.inputs[inputIndex]!;
|
|
312
374
|
if (input.nonWitnessUtxo) {
|
|
313
|
-
addNonWitnessTxCache(this
|
|
375
|
+
addNonWitnessTxCache(this.#cache, input, inputIndex);
|
|
314
376
|
}
|
|
315
|
-
c.
|
|
316
|
-
c.
|
|
317
|
-
c.
|
|
377
|
+
c.fee = undefined;
|
|
378
|
+
c.feeRate = undefined;
|
|
379
|
+
c.extractedTx = undefined;
|
|
380
|
+
c.prevOuts = undefined;
|
|
381
|
+
c.signingScripts = undefined;
|
|
382
|
+
c.values = undefined;
|
|
383
|
+
c.taprootHashCache = undefined;
|
|
318
384
|
return this;
|
|
319
385
|
}
|
|
320
386
|
|
|
321
|
-
addOutputs(outputDatas: PsbtOutputExtended[]): this {
|
|
322
|
-
outputDatas.forEach((outputData) => this.addOutput(outputData));
|
|
387
|
+
addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs: boolean = true): this {
|
|
388
|
+
outputDatas.forEach((outputData) => this.addOutput(outputData, checkPartialSigs));
|
|
323
389
|
return this;
|
|
324
390
|
}
|
|
325
391
|
|
|
326
|
-
|
|
392
|
+
/**
|
|
393
|
+
* Add an output to the PSBT.
|
|
394
|
+
*
|
|
395
|
+
* **PERFORMANCE WARNING:** Passing an `address` string is ~10x slower than passing
|
|
396
|
+
* a `script` directly due to address parsing overhead (bech32 decode, etc.).
|
|
397
|
+
* For high-performance use cases with many outputs, pre-compute the script using
|
|
398
|
+
* `toOutputScript(address, network)` and pass `{ script, value }` instead.
|
|
399
|
+
*
|
|
400
|
+
* @param outputData - Output data with either `address` or `script`, and `value`
|
|
401
|
+
* @param checkPartialSigs - Whether to check for existing signatures (default: true)
|
|
402
|
+
*/
|
|
403
|
+
addOutput(outputData: PsbtOutputExtended, checkPartialSigs: boolean = true): this {
|
|
327
404
|
const hasAddress = 'address' in outputData;
|
|
328
405
|
const hasScript = 'script' in outputData;
|
|
329
|
-
if (
|
|
330
|
-
arguments.length > 1 ||
|
|
331
|
-
!outputData ||
|
|
332
|
-
outputData.value === undefined ||
|
|
333
|
-
(!hasAddress && !hasScript)
|
|
334
|
-
) {
|
|
406
|
+
if (!outputData || outputData.value === undefined || (!hasAddress && !hasScript)) {
|
|
335
407
|
throw new Error(
|
|
336
408
|
`Invalid arguments for Psbt.addOutput. ` +
|
|
337
409
|
`Requires single object with at least [script or address] and [value]`,
|
|
338
410
|
);
|
|
339
411
|
}
|
|
340
|
-
|
|
412
|
+
if (checkPartialSigs) {
|
|
413
|
+
checkInputsForPartialSig(this.data.inputs, 'addOutput', this.#cache.hasSignatures);
|
|
414
|
+
}
|
|
341
415
|
if (hasAddress) {
|
|
342
416
|
const { address } = outputData as PsbtOutputExtendedAddress;
|
|
343
|
-
const { network } = this
|
|
344
|
-
const script = toOutputScript(address, network);
|
|
417
|
+
const { network } = this.#opts;
|
|
418
|
+
const script = toOutputScript(address, network) as Script;
|
|
345
419
|
outputData = Object.assign({}, outputData, { script });
|
|
346
420
|
}
|
|
347
421
|
checkTaprootOutputFields(outputData, outputData, 'addOutput');
|
|
348
422
|
|
|
349
|
-
const c = this
|
|
423
|
+
const c = this.#cache;
|
|
350
424
|
this.data.addOutput(outputData);
|
|
351
|
-
c.
|
|
352
|
-
c.
|
|
353
|
-
c.
|
|
425
|
+
c.fee = undefined;
|
|
426
|
+
c.feeRate = undefined;
|
|
427
|
+
c.extractedTx = undefined;
|
|
428
|
+
c.taprootHashCache = undefined;
|
|
354
429
|
return this;
|
|
355
430
|
}
|
|
356
431
|
|
|
357
432
|
extractTransaction(disableFeeCheck?: boolean, disableOutputChecks?: boolean): Transaction {
|
|
358
433
|
if (disableOutputChecks) {
|
|
359
|
-
this.data.inputs = this.data.inputs.filter(
|
|
434
|
+
(this.data as unknown as { inputs: PsbtInput[] }).inputs = this.data.inputs.filter(
|
|
435
|
+
(i) => !i.partialSig,
|
|
436
|
+
);
|
|
360
437
|
}
|
|
361
438
|
|
|
362
439
|
if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
|
|
363
|
-
const c = this
|
|
440
|
+
const c = this.#cache;
|
|
364
441
|
if (!disableFeeCheck) {
|
|
365
|
-
checkFees(this, c, this
|
|
442
|
+
checkFees(this, c, this.#opts);
|
|
366
443
|
}
|
|
367
|
-
if (c.
|
|
368
|
-
const tx = c.
|
|
444
|
+
if (c.extractedTx) return c.extractedTx;
|
|
445
|
+
const tx = c.tx.clone();
|
|
369
446
|
inputFinalizeGetAmts(this.data.inputs, tx, c, true, disableOutputChecks);
|
|
370
447
|
return tx;
|
|
371
448
|
}
|
|
372
449
|
|
|
373
450
|
getFeeRate(disableOutputChecks: boolean = false): number {
|
|
374
451
|
return getTxCacheValue(
|
|
375
|
-
'
|
|
452
|
+
'feeRate',
|
|
376
453
|
'fee rate',
|
|
377
454
|
this.data.inputs,
|
|
378
|
-
this
|
|
455
|
+
this.#cache,
|
|
379
456
|
disableOutputChecks,
|
|
380
457
|
);
|
|
381
458
|
}
|
|
382
459
|
|
|
383
460
|
getFee(disableOutputChecks: boolean = false): number {
|
|
384
|
-
return getTxCacheValue('
|
|
461
|
+
return getTxCacheValue('fee', 'fee', this.data.inputs, this.#cache, disableOutputChecks);
|
|
385
462
|
}
|
|
386
463
|
|
|
387
464
|
finalizeAllInputs(): this {
|
|
@@ -397,14 +474,14 @@ export class Psbt {
|
|
|
397
474
|
): this {
|
|
398
475
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
399
476
|
if (isTaprootInput(input)) {
|
|
400
|
-
return this
|
|
477
|
+
return this.#finalizeTaprootInput(
|
|
401
478
|
inputIndex,
|
|
402
479
|
input,
|
|
403
480
|
undefined,
|
|
404
481
|
finalScriptsFunc as FinalTaprootScriptsFunc,
|
|
405
482
|
);
|
|
406
483
|
}
|
|
407
|
-
return this
|
|
484
|
+
return this.#finalizeInput(
|
|
408
485
|
inputIndex,
|
|
409
486
|
input,
|
|
410
487
|
finalScriptsFunc as FinalScriptsFunc,
|
|
@@ -414,12 +491,12 @@ export class Psbt {
|
|
|
414
491
|
|
|
415
492
|
finalizeTaprootInput(
|
|
416
493
|
inputIndex: number,
|
|
417
|
-
tapLeafHashToFinalize?:
|
|
494
|
+
tapLeafHashToFinalize?: Bytes32,
|
|
418
495
|
finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
|
|
419
496
|
): this {
|
|
420
497
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
421
498
|
if (isTaprootInput(input))
|
|
422
|
-
return this
|
|
499
|
+
return this.#finalizeTaprootInput(
|
|
423
500
|
inputIndex,
|
|
424
501
|
input,
|
|
425
502
|
tapLeafHashToFinalize,
|
|
@@ -430,7 +507,7 @@ export class Psbt {
|
|
|
430
507
|
|
|
431
508
|
getInputType(inputIndex: number): AllScriptType {
|
|
432
509
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
433
|
-
const script = getScriptFromUtxo(inputIndex, input, this
|
|
510
|
+
const script = getScriptFromUtxo(inputIndex, input, this.#cache);
|
|
434
511
|
const result = getMeaningfulScript(
|
|
435
512
|
script,
|
|
436
513
|
inputIndex,
|
|
@@ -443,9 +520,9 @@ export class Psbt {
|
|
|
443
520
|
return (type + mainType) as AllScriptType;
|
|
444
521
|
}
|
|
445
522
|
|
|
446
|
-
inputHasPubkey(inputIndex: number, pubkey:
|
|
523
|
+
inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean {
|
|
447
524
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
448
|
-
return pubkeyInInput(pubkey, input, inputIndex, this
|
|
525
|
+
return pubkeyInInput(pubkey, input, inputIndex, this.#cache);
|
|
449
526
|
}
|
|
450
527
|
|
|
451
528
|
inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
|
|
@@ -454,9 +531,9 @@ export class Psbt {
|
|
|
454
531
|
return !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine);
|
|
455
532
|
}
|
|
456
533
|
|
|
457
|
-
outputHasPubkey(outputIndex: number, pubkey:
|
|
534
|
+
outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean {
|
|
458
535
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
459
|
-
return pubkeyInOutput(pubkey, output, outputIndex, this
|
|
536
|
+
return pubkeyInOutput(pubkey, output, outputIndex, this.#cache);
|
|
460
537
|
}
|
|
461
538
|
|
|
462
539
|
outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
|
|
@@ -476,13 +553,13 @@ export class Psbt {
|
|
|
476
553
|
validateSignaturesOfInput(
|
|
477
554
|
inputIndex: number,
|
|
478
555
|
validator: ValidateSigFunction,
|
|
479
|
-
pubkey?:
|
|
556
|
+
pubkey?: PublicKey,
|
|
480
557
|
): boolean {
|
|
481
|
-
const input = this.data.inputs[inputIndex]
|
|
558
|
+
const input = this.data.inputs[inputIndex]!;
|
|
482
559
|
if (isTaprootInput(input))
|
|
483
|
-
return this
|
|
560
|
+
return this.#validateSignaturesOfTaprootInput(inputIndex, validator, pubkey);
|
|
484
561
|
|
|
485
|
-
return this
|
|
562
|
+
return this.#validateSignaturesOfInput(inputIndex, validator, pubkey);
|
|
486
563
|
}
|
|
487
564
|
|
|
488
565
|
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes: number[] = [Transaction.SIGHASH_ALL]): this {
|
|
@@ -545,7 +622,7 @@ export class Psbt {
|
|
|
545
622
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
546
623
|
throw new Error('Need HDSigner to sign input');
|
|
547
624
|
}
|
|
548
|
-
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair)
|
|
625
|
+
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
549
626
|
signers.forEach((signer) => this.signInput(inputIndex, signer, sighashTypes));
|
|
550
627
|
return this;
|
|
551
628
|
}
|
|
@@ -561,7 +638,7 @@ export class Psbt {
|
|
|
561
638
|
}
|
|
562
639
|
const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
|
|
563
640
|
const promises = signers.map((signer) =>
|
|
564
|
-
this.signInputAsync(inputIndex, signer
|
|
641
|
+
this.signInputAsync(inputIndex, signer, sighashTypes),
|
|
565
642
|
);
|
|
566
643
|
return Promise.all(promises)
|
|
567
644
|
.then(() => {
|
|
@@ -631,7 +708,7 @@ export class Psbt {
|
|
|
631
708
|
|
|
632
709
|
signInput(
|
|
633
710
|
inputIndex: number,
|
|
634
|
-
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
711
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
|
|
635
712
|
sighashTypes?: number[],
|
|
636
713
|
): this {
|
|
637
714
|
if (!keyPair || !keyPair.publicKey) {
|
|
@@ -640,16 +717,16 @@ export class Psbt {
|
|
|
640
717
|
|
|
641
718
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
642
719
|
if (isTaprootInput(input)) {
|
|
643
|
-
return this
|
|
720
|
+
return this.#signTaprootInput(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
644
721
|
}
|
|
645
722
|
|
|
646
|
-
return this
|
|
723
|
+
return this.#signInput(inputIndex, keyPair, sighashTypes);
|
|
647
724
|
}
|
|
648
725
|
|
|
649
726
|
signTaprootInput(
|
|
650
727
|
inputIndex: number,
|
|
651
|
-
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
652
|
-
tapLeafHashToSign?:
|
|
728
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
|
|
729
|
+
tapLeafHashToSign?: Uint8Array,
|
|
653
730
|
sighashTypes?: number[],
|
|
654
731
|
): this {
|
|
655
732
|
if (!keyPair || !keyPair.publicKey) {
|
|
@@ -658,7 +735,7 @@ export class Psbt {
|
|
|
658
735
|
|
|
659
736
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
660
737
|
if (isTaprootInput(input)) {
|
|
661
|
-
return this
|
|
738
|
+
return this.#signTaprootInput(
|
|
662
739
|
inputIndex,
|
|
663
740
|
input,
|
|
664
741
|
keyPair,
|
|
@@ -672,7 +749,14 @@ export class Psbt {
|
|
|
672
749
|
|
|
673
750
|
signInputAsync(
|
|
674
751
|
inputIndex: number,
|
|
675
|
-
keyPair:
|
|
752
|
+
keyPair:
|
|
753
|
+
| Signer
|
|
754
|
+
| SignerAlternative
|
|
755
|
+
| SignerAsync
|
|
756
|
+
| HDSigner
|
|
757
|
+
| HDSignerAsync
|
|
758
|
+
| BIP32Interface
|
|
759
|
+
| ECPairInterface,
|
|
676
760
|
sighashTypes?: number[],
|
|
677
761
|
): Promise<void> {
|
|
678
762
|
return Promise.resolve().then(() => {
|
|
@@ -680,7 +764,7 @@ export class Psbt {
|
|
|
680
764
|
|
|
681
765
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
682
766
|
if (isTaprootInput(input))
|
|
683
|
-
return this
|
|
767
|
+
return this.#signTaprootInputAsync(
|
|
684
768
|
inputIndex,
|
|
685
769
|
input,
|
|
686
770
|
keyPair,
|
|
@@ -688,14 +772,21 @@ export class Psbt {
|
|
|
688
772
|
sighashTypes,
|
|
689
773
|
);
|
|
690
774
|
|
|
691
|
-
return this
|
|
775
|
+
return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
|
|
692
776
|
});
|
|
693
777
|
}
|
|
694
778
|
|
|
695
779
|
signTaprootInputAsync(
|
|
696
780
|
inputIndex: number,
|
|
697
|
-
keyPair:
|
|
698
|
-
|
|
781
|
+
keyPair:
|
|
782
|
+
| Signer
|
|
783
|
+
| SignerAlternative
|
|
784
|
+
| SignerAsync
|
|
785
|
+
| HDSigner
|
|
786
|
+
| HDSignerAsync
|
|
787
|
+
| BIP32Interface
|
|
788
|
+
| ECPairInterface,
|
|
789
|
+
tapLeafHash?: Uint8Array,
|
|
699
790
|
sighashTypes?: number[],
|
|
700
791
|
): Promise<void> {
|
|
701
792
|
return Promise.resolve().then(() => {
|
|
@@ -703,7 +794,7 @@ export class Psbt {
|
|
|
703
794
|
|
|
704
795
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
705
796
|
if (isTaprootInput(input))
|
|
706
|
-
return this
|
|
797
|
+
return this.#signTaprootInputAsync(
|
|
707
798
|
inputIndex,
|
|
708
799
|
input,
|
|
709
800
|
keyPair,
|
|
@@ -715,18 +806,18 @@ export class Psbt {
|
|
|
715
806
|
});
|
|
716
807
|
}
|
|
717
808
|
|
|
718
|
-
toBuffer():
|
|
719
|
-
checkCache(this
|
|
720
|
-
return this.data.toBuffer();
|
|
809
|
+
toBuffer(): Uint8Array {
|
|
810
|
+
checkCache(this.#cache);
|
|
811
|
+
return new Uint8Array(this.data.toBuffer());
|
|
721
812
|
}
|
|
722
813
|
|
|
723
814
|
toHex(): string {
|
|
724
|
-
checkCache(this
|
|
815
|
+
checkCache(this.#cache);
|
|
725
816
|
return this.data.toHex();
|
|
726
817
|
}
|
|
727
818
|
|
|
728
819
|
toBase64(): string {
|
|
729
|
-
checkCache(this
|
|
820
|
+
checkCache(this.#cache);
|
|
730
821
|
return this.data.toBase64();
|
|
731
822
|
}
|
|
732
823
|
|
|
@@ -737,16 +828,29 @@ export class Psbt {
|
|
|
737
828
|
|
|
738
829
|
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
|
|
739
830
|
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
|
|
740
|
-
checkTaprootInputFields(this.data.inputs[inputIndex]
|
|
741
|
-
|
|
831
|
+
checkTaprootInputFields(this.data.inputs[inputIndex]!, updateData, 'updateInput');
|
|
832
|
+
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
833
|
+
const normalizedUpdate = updateData.witnessUtxo
|
|
834
|
+
? {
|
|
835
|
+
...updateData,
|
|
836
|
+
witnessUtxo: {
|
|
837
|
+
script: updateData.witnessUtxo.script,
|
|
838
|
+
value:
|
|
839
|
+
typeof updateData.witnessUtxo.value === 'bigint'
|
|
840
|
+
? updateData.witnessUtxo.value
|
|
841
|
+
: BigInt(updateData.witnessUtxo.value),
|
|
842
|
+
},
|
|
843
|
+
}
|
|
844
|
+
: updateData;
|
|
845
|
+
this.data.updateInput(inputIndex, normalizedUpdate);
|
|
742
846
|
if (updateData.nonWitnessUtxo) {
|
|
743
|
-
addNonWitnessTxCache(this
|
|
847
|
+
addNonWitnessTxCache(this.#cache, this.data.inputs[inputIndex]!, inputIndex);
|
|
744
848
|
}
|
|
745
849
|
return this;
|
|
746
850
|
}
|
|
747
851
|
|
|
748
852
|
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
|
|
749
|
-
const outputData = this.data.outputs[outputIndex]
|
|
853
|
+
const outputData = this.data.outputs[outputIndex]!;
|
|
750
854
|
checkTaprootOutputFields(outputData, updateData, 'updateOutput');
|
|
751
855
|
|
|
752
856
|
this.data.updateOutput(outputIndex, updateData);
|
|
@@ -773,39 +877,46 @@ export class Psbt {
|
|
|
773
877
|
return this;
|
|
774
878
|
}
|
|
775
879
|
|
|
776
|
-
|
|
880
|
+
checkTaprootHashesForSig(
|
|
777
881
|
inputIndex: number,
|
|
778
882
|
input: PsbtInput,
|
|
779
|
-
keyPair:
|
|
780
|
-
|
|
883
|
+
keyPair:
|
|
884
|
+
| Signer
|
|
885
|
+
| SignerAlternative
|
|
886
|
+
| SignerAsync
|
|
887
|
+
| HDSigner
|
|
888
|
+
| HDSignerAsync
|
|
889
|
+
| TaprootHashCheckSigner
|
|
890
|
+
| BIP32Interface
|
|
891
|
+
| ECPairInterface,
|
|
892
|
+
tapLeafHashToSign?: Uint8Array,
|
|
781
893
|
allowedSighashTypes?: number[],
|
|
782
|
-
): { hash:
|
|
783
|
-
if (typeof keyPair.signSchnorr !== 'function')
|
|
894
|
+
): { hash: Bytes32; leafHash?: Bytes32 }[] {
|
|
895
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
784
896
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
785
897
|
|
|
786
|
-
const pubkey =
|
|
787
|
-
|
|
788
|
-
|
|
898
|
+
const pubkey =
|
|
899
|
+
keyPair.publicKey instanceof Uint8Array
|
|
900
|
+
? keyPair.publicKey
|
|
901
|
+
: new Uint8Array(keyPair.publicKey);
|
|
789
902
|
|
|
790
903
|
const hashesForSig = getTaprootHashesForSig(
|
|
791
904
|
inputIndex,
|
|
792
905
|
input,
|
|
793
906
|
this.data.inputs,
|
|
794
907
|
pubkey,
|
|
795
|
-
this
|
|
908
|
+
this.#cache,
|
|
796
909
|
tapLeafHashToSign,
|
|
797
910
|
allowedSighashTypes,
|
|
798
911
|
);
|
|
799
912
|
|
|
800
913
|
if (!hashesForSig || !hashesForSig.length)
|
|
801
|
-
throw new Error(
|
|
802
|
-
`Can not sign for input #${inputIndex} with the key ${pubkey.toString('hex')}`,
|
|
803
|
-
);
|
|
914
|
+
throw new Error(`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`);
|
|
804
915
|
|
|
805
916
|
return hashesForSig;
|
|
806
917
|
}
|
|
807
918
|
|
|
808
|
-
|
|
919
|
+
#finalizeInput(
|
|
809
920
|
inputIndex: number,
|
|
810
921
|
input: PsbtInput,
|
|
811
922
|
finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
|
|
@@ -814,7 +925,7 @@ export class Psbt {
|
|
|
814
925
|
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
|
|
815
926
|
inputIndex,
|
|
816
927
|
input,
|
|
817
|
-
this
|
|
928
|
+
this.#cache,
|
|
818
929
|
);
|
|
819
930
|
if (!script) throw new Error(`No script found for input #${inputIndex}`);
|
|
820
931
|
|
|
@@ -839,11 +950,11 @@ export class Psbt {
|
|
|
839
950
|
return this;
|
|
840
951
|
}
|
|
841
952
|
|
|
842
|
-
|
|
953
|
+
#finalizeTaprootInput(
|
|
843
954
|
inputIndex: number,
|
|
844
955
|
input: PsbtInput,
|
|
845
|
-
tapLeafHashToFinalize?:
|
|
846
|
-
finalScriptsFunc = tapScriptFinalizer,
|
|
956
|
+
tapLeafHashToFinalize?: Bytes32,
|
|
957
|
+
finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
|
|
847
958
|
): this {
|
|
848
959
|
if (!input.witnessUtxo)
|
|
849
960
|
throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
|
|
@@ -851,8 +962,8 @@ export class Psbt {
|
|
|
851
962
|
// Check key spend first. Increased privacy and reduced block space.
|
|
852
963
|
if (input.tapKeySig) {
|
|
853
964
|
const payment = payments.p2tr({
|
|
854
|
-
output: input.witnessUtxo.script,
|
|
855
|
-
signature: input.tapKeySig,
|
|
965
|
+
output: input.witnessUtxo.script as Script,
|
|
966
|
+
signature: input.tapKeySig as SchnorrSignature,
|
|
856
967
|
});
|
|
857
968
|
if (!payment.witness) throw new Error('Cannot finalize taproot key spend');
|
|
858
969
|
const finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
@@ -863,7 +974,7 @@ export class Psbt {
|
|
|
863
974
|
input,
|
|
864
975
|
tapLeafHashToFinalize,
|
|
865
976
|
);
|
|
866
|
-
this.data.updateInput(inputIndex, { finalScriptWitness });
|
|
977
|
+
this.data.updateInput(inputIndex, { finalScriptWitness } as PsbtInputUpdate);
|
|
867
978
|
}
|
|
868
979
|
|
|
869
980
|
this.data.clearFinalizedInput(inputIndex);
|
|
@@ -871,10 +982,10 @@ export class Psbt {
|
|
|
871
982
|
return this;
|
|
872
983
|
}
|
|
873
984
|
|
|
874
|
-
|
|
985
|
+
#validateSignaturesOfInput(
|
|
875
986
|
inputIndex: number,
|
|
876
987
|
validator: ValidateSigFunction,
|
|
877
|
-
pubkey?:
|
|
988
|
+
pubkey?: PublicKey,
|
|
878
989
|
): boolean {
|
|
879
990
|
const input = this.data.inputs[inputIndex];
|
|
880
991
|
const partialSig = (input || {}).partialSig;
|
|
@@ -882,14 +993,16 @@ export class Psbt {
|
|
|
882
993
|
throw new Error('No signatures to validate');
|
|
883
994
|
if (typeof validator !== 'function')
|
|
884
995
|
throw new Error('Need validator function to validate signatures');
|
|
885
|
-
const mySigs = pubkey ? partialSig.filter((sig) => sig.pubkey
|
|
996
|
+
const mySigs = pubkey ? partialSig.filter((sig) => equals(sig.pubkey, pubkey)) : partialSig;
|
|
886
997
|
if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
|
|
887
998
|
const results: boolean[] = [];
|
|
888
|
-
let hashCache:
|
|
889
|
-
let scriptCache:
|
|
999
|
+
let hashCache: Bytes32 | undefined;
|
|
1000
|
+
let scriptCache: Script | undefined;
|
|
890
1001
|
let sighashCache: number | undefined;
|
|
891
1002
|
for (const pSig of mySigs) {
|
|
892
|
-
const
|
|
1003
|
+
const pSigSignature = pSig.signature;
|
|
1004
|
+
const pSigPubkey = pSig.pubkey as PublicKey;
|
|
1005
|
+
const sig = bscript.signature.decode(pSigSignature);
|
|
893
1006
|
const { hash, script } =
|
|
894
1007
|
sighashCache !== sig.hashType || !hashCache || !scriptCache
|
|
895
1008
|
? getHashForSig(
|
|
@@ -897,25 +1010,25 @@ export class Psbt {
|
|
|
897
1010
|
Object.assign({}, input, {
|
|
898
1011
|
sighashType: sig.hashType,
|
|
899
1012
|
}),
|
|
900
|
-
this
|
|
1013
|
+
this.#cache,
|
|
901
1014
|
true,
|
|
902
1015
|
)
|
|
903
1016
|
: { hash: hashCache, script: scriptCache };
|
|
904
1017
|
sighashCache = sig.hashType;
|
|
905
1018
|
hashCache = hash;
|
|
906
1019
|
scriptCache = script;
|
|
907
|
-
checkScriptForPubkey(
|
|
908
|
-
results.push(validator(
|
|
1020
|
+
checkScriptForPubkey(pSigPubkey, script, 'verify');
|
|
1021
|
+
results.push(validator(pSigPubkey, hash, sig.signature));
|
|
909
1022
|
}
|
|
910
1023
|
return results.every((res) => res);
|
|
911
1024
|
}
|
|
912
1025
|
|
|
913
|
-
|
|
1026
|
+
#validateSignaturesOfTaprootInput(
|
|
914
1027
|
inputIndex: number,
|
|
915
1028
|
validator: ValidateSigFunction,
|
|
916
|
-
pubkey?:
|
|
1029
|
+
pubkey?: PublicKey,
|
|
917
1030
|
): boolean {
|
|
918
|
-
const input = this.data.inputs[inputIndex]
|
|
1031
|
+
const input = this.data.inputs[inputIndex]!;
|
|
919
1032
|
const tapKeySig = (input || {}).tapKeySig;
|
|
920
1033
|
const tapScriptSig = (input || {}).tapScriptSig;
|
|
921
1034
|
if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
|
|
@@ -923,10 +1036,10 @@ export class Psbt {
|
|
|
923
1036
|
if (typeof validator !== 'function')
|
|
924
1037
|
throw new Error('Need validator function to validate signatures');
|
|
925
1038
|
|
|
926
|
-
|
|
927
|
-
const allHashses =
|
|
928
|
-
? getTaprootHashesForSig(inputIndex, input, this.data.inputs,
|
|
929
|
-
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this
|
|
1039
|
+
const xPubkey = pubkey ? toXOnly(pubkey) : undefined;
|
|
1040
|
+
const allHashses = xPubkey
|
|
1041
|
+
? getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey, this.#cache)
|
|
1042
|
+
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this.#cache);
|
|
930
1043
|
|
|
931
1044
|
if (!allHashses.length) throw new Error('No signatures for this pubkey');
|
|
932
1045
|
|
|
@@ -944,10 +1057,11 @@ export class Psbt {
|
|
|
944
1057
|
|
|
945
1058
|
if (tapScriptSig) {
|
|
946
1059
|
for (const tapSig of tapScriptSig) {
|
|
947
|
-
const
|
|
1060
|
+
const tapSigPubkey = tapSig.pubkey as PublicKey;
|
|
1061
|
+
const tapSigHash = allHashses.find((h) => equals(tapSigPubkey, h.pubkey));
|
|
948
1062
|
if (tapSigHash) {
|
|
949
1063
|
const isValidTapScriptSig = validator(
|
|
950
|
-
|
|
1064
|
+
tapSigPubkey,
|
|
951
1065
|
tapSigHash.hash,
|
|
952
1066
|
trimTaprootSig(tapSig.signature),
|
|
953
1067
|
);
|
|
@@ -960,52 +1074,55 @@ export class Psbt {
|
|
|
960
1074
|
return validationResultCount > 0;
|
|
961
1075
|
}
|
|
962
1076
|
|
|
963
|
-
|
|
1077
|
+
#signInput(
|
|
964
1078
|
inputIndex: number,
|
|
965
|
-
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
1079
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
|
|
966
1080
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
967
1081
|
): this {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
: Buffer.from(keyPair.publicKey);
|
|
1082
|
+
const pubkey =
|
|
1083
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1084
|
+
? keyPair.publicKey
|
|
1085
|
+
: new Uint8Array(keyPair.publicKey);
|
|
973
1086
|
|
|
974
1087
|
const { hash, sighashType } = getHashAndSighashType(
|
|
975
1088
|
this.data.inputs,
|
|
976
1089
|
inputIndex,
|
|
977
1090
|
pubkey,
|
|
978
|
-
this
|
|
1091
|
+
this.#cache,
|
|
979
1092
|
sighashTypes,
|
|
980
1093
|
);
|
|
981
1094
|
|
|
982
|
-
const sig = keyPair.sign(hash);
|
|
1095
|
+
const sig = keyPair.sign(hash) as Uint8Array;
|
|
983
1096
|
const partialSig = [
|
|
984
1097
|
{
|
|
985
1098
|
pubkey,
|
|
986
1099
|
signature: bscript.signature.encode(
|
|
987
|
-
|
|
1100
|
+
sig instanceof Uint8Array ? sig : new Uint8Array(sig),
|
|
988
1101
|
sighashType,
|
|
989
1102
|
),
|
|
990
1103
|
},
|
|
991
1104
|
];
|
|
992
1105
|
|
|
993
1106
|
this.data.updateInput(inputIndex, { partialSig });
|
|
1107
|
+
this.#cache.hasSignatures = true;
|
|
994
1108
|
return this;
|
|
995
1109
|
}
|
|
996
1110
|
|
|
997
|
-
|
|
1111
|
+
#signTaprootInput(
|
|
998
1112
|
inputIndex: number,
|
|
999
1113
|
input: PsbtInput,
|
|
1000
|
-
keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
|
|
1001
|
-
tapLeafHashToSign?:
|
|
1114
|
+
keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
|
|
1115
|
+
tapLeafHashToSign?: Uint8Array,
|
|
1002
1116
|
allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
1003
1117
|
): this {
|
|
1004
|
-
|
|
1118
|
+
const pubkey = (
|
|
1119
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1120
|
+
? keyPair.publicKey
|
|
1121
|
+
: new Uint8Array(keyPair.publicKey)
|
|
1122
|
+
) as PublicKey;
|
|
1005
1123
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
: Buffer.from(keyPair.publicKey);
|
|
1124
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
1125
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
1009
1126
|
|
|
1010
1127
|
// checkTaprootHashesForSig validates signSchnorr exists
|
|
1011
1128
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
@@ -1015,25 +1132,22 @@ export class Psbt {
|
|
|
1015
1132
|
tapLeafHashToSign,
|
|
1016
1133
|
allowedSighashTypes,
|
|
1017
1134
|
);
|
|
1018
|
-
const signSchnorr = (keyPair.signSchnorr as (h:
|
|
1135
|
+
const signSchnorr = (keyPair.signSchnorr as (h: Uint8Array) => Uint8Array).bind(keyPair);
|
|
1019
1136
|
|
|
1020
|
-
const
|
|
1021
|
-
Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1022
|
-
|
|
1023
|
-
const tapKeySig: TapKeySig = hashesForSig
|
|
1137
|
+
const tapKeySig = hashesForSig
|
|
1024
1138
|
.filter((h) => !h.leafHash)
|
|
1025
1139
|
.map((h) =>
|
|
1026
|
-
serializeTaprootSignature(
|
|
1027
|
-
)[0];
|
|
1140
|
+
serializeTaprootSignature(signSchnorr(h.hash), input.sighashType),
|
|
1141
|
+
)[0] as TapKeySig;
|
|
1028
1142
|
|
|
1029
|
-
const tapScriptSig
|
|
1143
|
+
const tapScriptSig = hashesForSig
|
|
1030
1144
|
.filter((h) => !!h.leafHash)
|
|
1031
1145
|
.map(
|
|
1032
1146
|
(h) =>
|
|
1033
1147
|
({
|
|
1034
1148
|
pubkey: toXOnly(pubkey),
|
|
1035
1149
|
signature: serializeTaprootSignature(
|
|
1036
|
-
|
|
1150
|
+
signSchnorr(h.hash),
|
|
1037
1151
|
input.sighashType,
|
|
1038
1152
|
),
|
|
1039
1153
|
leafHash: h.leafHash,
|
|
@@ -1042,36 +1156,44 @@ export class Psbt {
|
|
|
1042
1156
|
|
|
1043
1157
|
if (tapKeySig) {
|
|
1044
1158
|
this.data.updateInput(inputIndex, { tapKeySig });
|
|
1159
|
+
this.#cache.hasSignatures = true;
|
|
1045
1160
|
}
|
|
1046
1161
|
|
|
1047
1162
|
if (tapScriptSig.length) {
|
|
1048
1163
|
this.data.updateInput(inputIndex, { tapScriptSig });
|
|
1164
|
+
this.#cache.hasSignatures = true;
|
|
1049
1165
|
}
|
|
1050
1166
|
|
|
1051
1167
|
return this;
|
|
1052
1168
|
}
|
|
1053
1169
|
|
|
1054
|
-
|
|
1170
|
+
#signInputAsync(
|
|
1055
1171
|
inputIndex: number,
|
|
1056
|
-
keyPair:
|
|
1172
|
+
keyPair:
|
|
1173
|
+
| Signer
|
|
1174
|
+
| SignerAlternative
|
|
1175
|
+
| SignerAsync
|
|
1176
|
+
| HDSigner
|
|
1177
|
+
| HDSignerAsync
|
|
1178
|
+
| BIP32Interface
|
|
1179
|
+
| ECPairInterface,
|
|
1057
1180
|
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
|
1058
1181
|
): Promise<void> {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
: Buffer.from(keyPair.publicKey);
|
|
1182
|
+
const pubkey =
|
|
1183
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1184
|
+
? keyPair.publicKey
|
|
1185
|
+
: new Uint8Array(keyPair.publicKey);
|
|
1064
1186
|
|
|
1065
1187
|
const { hash, sighashType } = getHashAndSighashType(
|
|
1066
1188
|
this.data.inputs,
|
|
1067
1189
|
inputIndex,
|
|
1068
1190
|
pubkey,
|
|
1069
|
-
this
|
|
1191
|
+
this.#cache,
|
|
1070
1192
|
sighashTypes,
|
|
1071
1193
|
);
|
|
1072
1194
|
|
|
1073
1195
|
return Promise.resolve(keyPair.sign(hash)).then((signature) => {
|
|
1074
|
-
const sig =
|
|
1196
|
+
const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
|
|
1075
1197
|
const partialSig = [
|
|
1076
1198
|
{
|
|
1077
1199
|
pubkey,
|
|
@@ -1080,21 +1202,32 @@ export class Psbt {
|
|
|
1080
1202
|
];
|
|
1081
1203
|
|
|
1082
1204
|
this.data.updateInput(inputIndex, { partialSig });
|
|
1205
|
+
this.#cache.hasSignatures = true;
|
|
1083
1206
|
});
|
|
1084
1207
|
}
|
|
1085
1208
|
|
|
1086
|
-
|
|
1209
|
+
async #signTaprootInputAsync(
|
|
1087
1210
|
inputIndex: number,
|
|
1088
1211
|
input: PsbtInput,
|
|
1089
|
-
keyPair:
|
|
1090
|
-
|
|
1212
|
+
keyPair:
|
|
1213
|
+
| Signer
|
|
1214
|
+
| SignerAlternative
|
|
1215
|
+
| SignerAsync
|
|
1216
|
+
| HDSigner
|
|
1217
|
+
| HDSignerAsync
|
|
1218
|
+
| BIP32Interface
|
|
1219
|
+
| ECPairInterface,
|
|
1220
|
+
tapLeafHash?: Uint8Array,
|
|
1091
1221
|
sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
|
|
1092
1222
|
): Promise<void> {
|
|
1093
|
-
|
|
1223
|
+
const pubkey = (
|
|
1224
|
+
keyPair.publicKey instanceof Uint8Array
|
|
1225
|
+
? keyPair.publicKey
|
|
1226
|
+
: new Uint8Array(keyPair.publicKey)
|
|
1227
|
+
) as PublicKey;
|
|
1094
1228
|
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
: Buffer.from(keyPair.publicKey);
|
|
1229
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
1230
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
1098
1231
|
|
|
1099
1232
|
// checkTaprootHashesForSig validates signSchnorr exists
|
|
1100
1233
|
const hashesForSig = this.checkTaprootHashesForSig(
|
|
@@ -1105,40 +1238,35 @@ export class Psbt {
|
|
|
1105
1238
|
sighashTypes,
|
|
1106
1239
|
);
|
|
1107
1240
|
const signSchnorr = (
|
|
1108
|
-
keyPair.signSchnorr as (hash:
|
|
1241
|
+
keyPair.signSchnorr as (hash: Uint8Array) => Uint8Array | Promise<Uint8Array>
|
|
1109
1242
|
).bind(keyPair);
|
|
1110
1243
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
const signaturePromises: Promise<
|
|
1115
|
-
{ tapKeySig: Buffer } | { tapScriptSig: TapScriptSig[] }
|
|
1116
|
-
>[] = [];
|
|
1244
|
+
type TapSignatureResult = { tapKeySig: Uint8Array } | { tapScriptSig: TapScriptSig[] };
|
|
1245
|
+
const signaturePromises: Promise<TapSignatureResult>[] = [];
|
|
1117
1246
|
|
|
1118
1247
|
const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
|
|
1119
1248
|
if (tapKeyHash) {
|
|
1120
1249
|
const tapKeySigPromise = Promise.resolve(signSchnorr(tapKeyHash.hash)).then((sig) => {
|
|
1121
1250
|
return {
|
|
1122
|
-
tapKeySig: serializeTaprootSignature(
|
|
1251
|
+
tapKeySig: serializeTaprootSignature(sig, input.sighashType),
|
|
1123
1252
|
};
|
|
1124
1253
|
});
|
|
1125
1254
|
signaturePromises.push(tapKeySigPromise);
|
|
1126
1255
|
}
|
|
1127
1256
|
|
|
1128
|
-
const tapScriptHashes = hashesForSig.filter(
|
|
1257
|
+
const tapScriptHashes = hashesForSig.filter(
|
|
1258
|
+
(h): h is typeof h & { leafHash: Bytes32 } => !!h.leafHash,
|
|
1259
|
+
);
|
|
1129
1260
|
if (tapScriptHashes.length) {
|
|
1130
1261
|
const tapScriptSigPromises = tapScriptHashes.map(async (tsh) => {
|
|
1131
1262
|
const signature = await signSchnorr(tsh.hash);
|
|
1132
1263
|
|
|
1133
|
-
const tapScriptSig = [
|
|
1264
|
+
const tapScriptSig: TapScriptSig[] = [
|
|
1134
1265
|
{
|
|
1135
1266
|
pubkey: toXOnly(pubkey),
|
|
1136
|
-
signature: serializeTaprootSignature(
|
|
1137
|
-
toBuffer(signature),
|
|
1138
|
-
input.sighashType,
|
|
1139
|
-
),
|
|
1267
|
+
signature: serializeTaprootSignature(signature, input.sighashType),
|
|
1140
1268
|
leafHash: tsh.leafHash,
|
|
1141
|
-
}
|
|
1269
|
+
},
|
|
1142
1270
|
];
|
|
1143
1271
|
|
|
1144
1272
|
return { tapScriptSig };
|
|
@@ -1148,124 +1276,18 @@ export class Psbt {
|
|
|
1148
1276
|
|
|
1149
1277
|
const results = await Promise.all(signaturePromises);
|
|
1150
1278
|
for (const v of results) {
|
|
1151
|
-
this.data.updateInput(inputIndex, v);
|
|
1279
|
+
this.data.updateInput(inputIndex, v as PsbtInputUpdate);
|
|
1280
|
+
this.#cache.hasSignatures = true;
|
|
1152
1281
|
}
|
|
1153
1282
|
}
|
|
1154
1283
|
}
|
|
1155
1284
|
|
|
1156
|
-
interface PsbtCache {
|
|
1157
|
-
__NON_WITNESS_UTXO_TX_CACHE: Transaction[];
|
|
1158
|
-
__NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
|
|
1159
|
-
__TX_IN_CACHE: { [index: string]: number };
|
|
1160
|
-
__TX: Transaction;
|
|
1161
|
-
__FEE_RATE?: number;
|
|
1162
|
-
__FEE?: number;
|
|
1163
|
-
__EXTRACTED_TX?: Transaction;
|
|
1164
|
-
__UNSAFE_SIGN_NONSEGWIT: boolean;
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
export interface PsbtOptsOptional {
|
|
1168
|
-
network?: Network;
|
|
1169
|
-
maximumFeeRate?: number;
|
|
1170
|
-
version?: 1 | 2 | 3;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
export interface PsbtOpts {
|
|
1174
|
-
network: Network;
|
|
1175
|
-
maximumFeeRate: number;
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
export interface PsbtInputExtended extends PsbtInput, TransactionInput {
|
|
1179
|
-
isPayToAnchor?: boolean;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
export type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
|
|
1183
|
-
|
|
1184
|
-
export interface PsbtOutputExtendedAddress extends PsbtOutput {
|
|
1185
|
-
address: string;
|
|
1186
|
-
value: number;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
export interface PsbtOutputExtendedScript extends PsbtOutput {
|
|
1190
|
-
script: Buffer;
|
|
1191
|
-
value: number;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
interface HDSignerBase {
|
|
1195
|
-
/**
|
|
1196
|
-
* DER format compressed publicKey buffer
|
|
1197
|
-
*/
|
|
1198
|
-
publicKey: Buffer;
|
|
1199
|
-
/**
|
|
1200
|
-
* The first 4 bytes of the sha256-ripemd160 of the publicKey
|
|
1201
|
-
*/
|
|
1202
|
-
fingerprint: Buffer;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
export interface HDSigner extends HDSignerBase {
|
|
1206
|
-
/**
|
|
1207
|
-
* The path string must match /^m(\/\d+'?)+$/
|
|
1208
|
-
* ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
|
|
1209
|
-
*/
|
|
1210
|
-
derivePath(path: string): HDSigner;
|
|
1211
|
-
|
|
1212
|
-
/**
|
|
1213
|
-
* Input hash (the "message digest") for the signature algorithm
|
|
1214
|
-
* Return a 64 byte signature (32 byte r and 32 byte s in that order)
|
|
1215
|
-
*/
|
|
1216
|
-
sign(hash: Buffer): Buffer;
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
/**
|
|
1220
|
-
* Same as above but with async sign method
|
|
1221
|
-
*/
|
|
1222
|
-
export interface HDSignerAsync extends HDSignerBase {
|
|
1223
|
-
derivePath(path: string): HDSignerAsync;
|
|
1224
|
-
|
|
1225
|
-
sign(hash: Buffer): Promise<Buffer>;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
export interface SignerAlternative {
|
|
1229
|
-
publicKey: Buffer;
|
|
1230
|
-
lowR: boolean;
|
|
1231
|
-
|
|
1232
|
-
sign(hash: Buffer, lowR?: boolean): Buffer;
|
|
1233
|
-
|
|
1234
|
-
verify(hash: Buffer, signature: Buffer): boolean;
|
|
1235
|
-
|
|
1236
|
-
signSchnorr(hash: Buffer): Buffer;
|
|
1237
|
-
|
|
1238
|
-
verifySchnorr(hash: Buffer, signature: Buffer): boolean;
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
export interface Signer {
|
|
1242
|
-
publicKey: Buffer;
|
|
1243
|
-
network?: Network;
|
|
1244
|
-
|
|
1245
|
-
sign(hash: Buffer, lowR?: boolean): Buffer;
|
|
1246
|
-
|
|
1247
|
-
signSchnorr?(hash: Buffer): Buffer;
|
|
1248
|
-
|
|
1249
|
-
getPublicKey?(): Buffer;
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
export interface SignerAsync {
|
|
1253
|
-
publicKey: Buffer;
|
|
1254
|
-
network?: Network;
|
|
1255
|
-
|
|
1256
|
-
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
|
|
1257
|
-
|
|
1258
|
-
signSchnorr?(hash: Buffer): Promise<Buffer>;
|
|
1259
|
-
|
|
1260
|
-
getPublicKey?(): Buffer;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
1285
|
/**
|
|
1264
1286
|
* This function is needed to pass to the bip174 base class's fromBuffer.
|
|
1265
1287
|
* It takes the "transaction buffer" portion of the psbt buffer and returns a
|
|
1266
1288
|
* Transaction (From the bip174 library) interface.
|
|
1267
1289
|
*/
|
|
1268
|
-
const transactionFromBuffer: TransactionFromBuffer = (buffer:
|
|
1290
|
+
const transactionFromBuffer: TransactionFromBuffer = (buffer: Uint8Array): ITransaction =>
|
|
1269
1291
|
new PsbtTransaction(buffer);
|
|
1270
1292
|
|
|
1271
1293
|
/**
|
|
@@ -1275,7 +1297,7 @@ const transactionFromBuffer: TransactionFromBuffer = (buffer: Buffer): ITransact
|
|
|
1275
1297
|
class PsbtTransaction implements ITransaction {
|
|
1276
1298
|
tx: Transaction;
|
|
1277
1299
|
|
|
1278
|
-
constructor(buffer:
|
|
1300
|
+
constructor(buffer: Uint8Array = new Uint8Array([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
|
1279
1301
|
this.tx = Transaction.fromBuffer(buffer);
|
|
1280
1302
|
checkTxEmpty(this.tx);
|
|
1281
1303
|
Object.defineProperty(this, 'tx', {
|
|
@@ -1298,15 +1320,14 @@ class PsbtTransaction implements ITransaction {
|
|
|
1298
1320
|
if (
|
|
1299
1321
|
input.hash === undefined ||
|
|
1300
1322
|
input.index === undefined ||
|
|
1301
|
-
(!
|
|
1323
|
+
(!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
|
|
1302
1324
|
typeof input.index !== 'number'
|
|
1303
1325
|
) {
|
|
1304
1326
|
throw new Error('Error adding input.');
|
|
1305
1327
|
}
|
|
1306
|
-
const hash =
|
|
1307
|
-
typeof input.hash === 'string'
|
|
1308
|
-
|
|
1309
|
-
: input.hash;
|
|
1328
|
+
const hash = (
|
|
1329
|
+
typeof input.hash === 'string' ? reverse(fromHex(input.hash)) : input.hash
|
|
1330
|
+
) as Bytes32;
|
|
1310
1331
|
|
|
1311
1332
|
this.tx.addInput(hash, input.index, input.sequence);
|
|
1312
1333
|
}
|
|
@@ -1315,20 +1336,20 @@ class PsbtTransaction implements ITransaction {
|
|
|
1315
1336
|
if (
|
|
1316
1337
|
output.script === undefined ||
|
|
1317
1338
|
output.value === undefined ||
|
|
1318
|
-
!
|
|
1319
|
-
typeof output.value !== '
|
|
1339
|
+
!(output.script instanceof Uint8Array) ||
|
|
1340
|
+
typeof output.value !== 'bigint'
|
|
1320
1341
|
) {
|
|
1321
1342
|
throw new Error('Error adding output.');
|
|
1322
1343
|
}
|
|
1323
1344
|
this.tx.addOutput(output.script, output.value);
|
|
1324
1345
|
}
|
|
1325
1346
|
|
|
1326
|
-
toBuffer():
|
|
1347
|
+
toBuffer(): Uint8Array {
|
|
1327
1348
|
return this.tx.toBuffer();
|
|
1328
1349
|
}
|
|
1329
1350
|
}
|
|
1330
1351
|
|
|
1331
|
-
function canFinalize(input: PsbtInput, script:
|
|
1352
|
+
function canFinalize(input: PsbtInput, script: Uint8Array, scriptType: string): boolean {
|
|
1332
1353
|
switch (scriptType) {
|
|
1333
1354
|
case 'pubkey':
|
|
1334
1355
|
case 'pubkeyhash':
|
|
@@ -1336,9 +1357,10 @@ function canFinalize(input: PsbtInput, script: Buffer, scriptType: string): bool
|
|
|
1336
1357
|
return hasSigs(1, input.partialSig);
|
|
1337
1358
|
case 'multisig': {
|
|
1338
1359
|
const p2ms = payments.p2ms({
|
|
1339
|
-
output: script,
|
|
1360
|
+
output: script as Script,
|
|
1340
1361
|
});
|
|
1341
|
-
|
|
1362
|
+
if (p2ms.m === undefined) throw new Error('Cannot determine m for multisig');
|
|
1363
|
+
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
|
|
1342
1364
|
}
|
|
1343
1365
|
case 'nonstandard':
|
|
1344
1366
|
return true;
|
|
@@ -1347,20 +1369,14 @@ function canFinalize(input: PsbtInput, script: Buffer, scriptType: string): bool
|
|
|
1347
1369
|
}
|
|
1348
1370
|
}
|
|
1349
1371
|
|
|
1350
|
-
function
|
|
1351
|
-
if (cache.__UNSAFE_SIGN_NONSEGWIT) {
|
|
1352
|
-
throw new Error('Not BIP174 compliant, can not export');
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
function hasSigs(neededSigs: number, partialSig?: PartialSig[], pubkeys?: Buffer[]): boolean {
|
|
1372
|
+
function hasSigs(neededSigs: number, partialSig?: PartialSig[], pubkeys?: Uint8Array[]): boolean {
|
|
1357
1373
|
if (!partialSig) return false;
|
|
1358
1374
|
let sigs: PartialSig[];
|
|
1359
1375
|
if (pubkeys) {
|
|
1360
1376
|
sigs = pubkeys
|
|
1361
1377
|
.map((pkey) => {
|
|
1362
1378
|
const pubkey = compressPubkey(pkey);
|
|
1363
|
-
return partialSig.find((pSig) => pSig.pubkey
|
|
1379
|
+
return partialSig.find((pSig) => equals(pSig.pubkey, pubkey));
|
|
1364
1380
|
})
|
|
1365
1381
|
.filter((v): v is PartialSig => !!v);
|
|
1366
1382
|
} else {
|
|
@@ -1370,32 +1386,25 @@ function hasSigs(neededSigs: number, partialSig?: PartialSig[], pubkeys?: Buffer
|
|
|
1370
1386
|
return sigs.length === neededSigs;
|
|
1371
1387
|
}
|
|
1372
1388
|
|
|
1373
|
-
function isFinalized(input: PsbtInput): boolean {
|
|
1374
|
-
return !!input.finalScriptSig || !!input.finalScriptWitness;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
1389
|
function bip32DerivationIsMine(root: HDSigner): (d: Bip32Derivation) => boolean {
|
|
1378
1390
|
return (d: Bip32Derivation): boolean => {
|
|
1379
|
-
const fingerprint =
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1391
|
+
const fingerprint =
|
|
1392
|
+
root.fingerprint instanceof Uint8Array
|
|
1393
|
+
? root.fingerprint
|
|
1394
|
+
: new Uint8Array(root.fingerprint);
|
|
1395
|
+
if (!equals(d.masterFingerprint, fingerprint)) return false;
|
|
1383
1396
|
const derivedPubkey = root.derivePath(d.path).publicKey;
|
|
1384
|
-
const pubkey =
|
|
1385
|
-
|
|
1397
|
+
const pubkey =
|
|
1398
|
+
derivedPubkey instanceof Uint8Array ? derivedPubkey : new Uint8Array(derivedPubkey);
|
|
1399
|
+
if (!equals(pubkey, d.pubkey)) return false;
|
|
1386
1400
|
return true;
|
|
1387
1401
|
};
|
|
1388
1402
|
}
|
|
1389
1403
|
|
|
1390
|
-
function check32Bit(num: number): void {
|
|
1391
|
-
if (typeof num !== 'number' || num !== Math.floor(num) || num > 0xffffffff || num < 0) {
|
|
1392
|
-
throw new Error('Invalid 32 bit integer');
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1404
|
function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
|
|
1397
|
-
const feeRate = cache.
|
|
1398
|
-
|
|
1405
|
+
const feeRate = cache.feeRate || psbt.getFeeRate();
|
|
1406
|
+
if (!cache.extractedTx) throw new Error('Transaction not extracted');
|
|
1407
|
+
const vsize = cache.extractedTx.virtualSize();
|
|
1399
1408
|
const satoshis = feeRate * vsize;
|
|
1400
1409
|
if (feeRate >= opts.maximumFeeRate) {
|
|
1401
1410
|
throw new Error(
|
|
@@ -1408,84 +1417,6 @@ function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
|
|
|
1408
1417
|
}
|
|
1409
1418
|
}
|
|
1410
1419
|
|
|
1411
|
-
function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
|
|
1412
|
-
inputs.forEach((input) => {
|
|
1413
|
-
const throws = isTaprootInput(input)
|
|
1414
|
-
? checkTaprootInputForSigs(input, action)
|
|
1415
|
-
: checkInputForSig(input, action);
|
|
1416
|
-
if (throws) throw new Error('Can not modify transaction, signatures exist.');
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
function checkPartialSigSighashes(input: PsbtInput): void {
|
|
1421
|
-
if (!input.sighashType || !input.partialSig) return;
|
|
1422
|
-
const { partialSig, sighashType } = input;
|
|
1423
|
-
partialSig.forEach((pSig) => {
|
|
1424
|
-
const { hashType } = bscript.signature.decode(pSig.signature);
|
|
1425
|
-
if (sighashType !== hashType) {
|
|
1426
|
-
throw new Error('Signature sighash does not match input sighash type');
|
|
1427
|
-
}
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
function checkScriptForPubkey(pubkey: Buffer, script: Buffer, action: string): void {
|
|
1432
|
-
if (!pubkeyInScript(pubkey, script)) {
|
|
1433
|
-
throw new Error(`Can not ${action} for this input with the key ${pubkey.toString('hex')}`);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
function checkTxEmpty(tx: Transaction): void {
|
|
1438
|
-
const isEmpty = tx.ins.every(
|
|
1439
|
-
(input) =>
|
|
1440
|
-
input.script &&
|
|
1441
|
-
input.script.length === 0 &&
|
|
1442
|
-
input.witness &&
|
|
1443
|
-
input.witness.length === 0,
|
|
1444
|
-
);
|
|
1445
|
-
if (!isEmpty) {
|
|
1446
|
-
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
|
|
1451
|
-
tx.ins.forEach((input) => {
|
|
1452
|
-
checkTxInputCache(cache, input);
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
function checkTxInputCache(cache: PsbtCache, input: { hash: Buffer; index: number }): void {
|
|
1457
|
-
const key = `${reverseBuffer(Buffer.from(input.hash)).toString('hex')}:${input.index}`;
|
|
1458
|
-
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
|
|
1459
|
-
cache.__TX_IN_CACHE[key] = 1;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
function scriptCheckerFactory(
|
|
1463
|
-
payment: (a: Payment, opts?: PaymentOpts) => Payment,
|
|
1464
|
-
paymentScriptName: string,
|
|
1465
|
-
): (idx: number, scriptPubKey: Buffer, redeemScript: Buffer, ioType: 'input' | 'output') => void {
|
|
1466
|
-
return (
|
|
1467
|
-
inputIndex: number,
|
|
1468
|
-
scriptPubKey: Buffer,
|
|
1469
|
-
redeemScript: Buffer,
|
|
1470
|
-
ioType: 'input' | 'output',
|
|
1471
|
-
): void => {
|
|
1472
|
-
const redeemScriptOutput = payment({
|
|
1473
|
-
redeem: { output: redeemScript },
|
|
1474
|
-
} as P2SHPayment).output as Buffer;
|
|
1475
|
-
|
|
1476
|
-
if (!scriptPubKey.equals(redeemScriptOutput)) {
|
|
1477
|
-
throw new Error(
|
|
1478
|
-
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
|
|
1479
|
-
);
|
|
1480
|
-
}
|
|
1481
|
-
};
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
|
|
1485
|
-
const checkWitnessScript = scriptCheckerFactory(payments.p2wsh, 'Witness script');
|
|
1486
|
-
|
|
1487
|
-
type TxCacheNumberKey = '__FEE_RATE' | '__FEE';
|
|
1488
|
-
|
|
1489
1420
|
function getTxCacheValue(
|
|
1490
1421
|
key: TxCacheNumberKey,
|
|
1491
1422
|
name: string,
|
|
@@ -1494,70 +1425,45 @@ function getTxCacheValue(
|
|
|
1494
1425
|
disableOutputChecks: boolean = false,
|
|
1495
1426
|
): number {
|
|
1496
1427
|
if (!inputs.every(isFinalized)) throw new Error(`PSBT must be finalized to calculate ${name}`);
|
|
1497
|
-
if (key === '
|
|
1498
|
-
if (key === '
|
|
1428
|
+
if (key === 'feeRate' && c.feeRate) return c.feeRate;
|
|
1429
|
+
if (key === 'fee' && c.fee) return c.fee;
|
|
1499
1430
|
let tx: Transaction;
|
|
1500
1431
|
let mustFinalize = true;
|
|
1501
|
-
if (c.
|
|
1502
|
-
tx = c.
|
|
1432
|
+
if (c.extractedTx) {
|
|
1433
|
+
tx = c.extractedTx;
|
|
1503
1434
|
mustFinalize = false;
|
|
1504
1435
|
} else {
|
|
1505
|
-
tx = c.
|
|
1436
|
+
tx = c.tx.clone();
|
|
1506
1437
|
}
|
|
1507
1438
|
inputFinalizeGetAmts(inputs, tx, c, mustFinalize, disableOutputChecks);
|
|
1508
|
-
const value = key === '
|
|
1439
|
+
const value = key === 'feeRate' ? c.feeRate : c.fee;
|
|
1509
1440
|
if (value === undefined) throw new Error(`Failed to calculate ${name}`);
|
|
1510
1441
|
return value;
|
|
1511
1442
|
}
|
|
1512
1443
|
|
|
1513
|
-
/**
|
|
1514
|
-
* This function must do two things:
|
|
1515
|
-
* 1. Check if the `input` can be finalized. If it can not be finalized, throw.
|
|
1516
|
-
* ie. `Can not finalize input #${inputIndex}`
|
|
1517
|
-
* 2. Create the finalScriptSig and finalScriptWitness Buffers.
|
|
1518
|
-
*/
|
|
1519
|
-
type FinalScriptsFunc = (
|
|
1520
|
-
inputIndex: number, // Which input is it?
|
|
1521
|
-
input: PsbtInput, // The PSBT input contents
|
|
1522
|
-
script: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
|
|
1523
|
-
isSegwit: boolean, // Is it segwit?
|
|
1524
|
-
isP2SH: boolean, // Is it P2SH?
|
|
1525
|
-
isP2WSH: boolean, // Is it P2WSH?
|
|
1526
|
-
canRunChecks: boolean,
|
|
1527
|
-
) => {
|
|
1528
|
-
finalScriptSig: Buffer | undefined;
|
|
1529
|
-
finalScriptWitness: Buffer | undefined;
|
|
1530
|
-
};
|
|
1531
|
-
type FinalTaprootScriptsFunc = (
|
|
1532
|
-
inputIndex: number, // Which input is it?
|
|
1533
|
-
input: PsbtInput, // The PSBT input contents
|
|
1534
|
-
tapLeafHashToFinalize?: Buffer, // Only finalize this specific leaf
|
|
1535
|
-
) => {
|
|
1536
|
-
finalScriptWitness: Buffer | undefined;
|
|
1537
|
-
};
|
|
1538
|
-
|
|
1539
1444
|
export function getFinalScripts(
|
|
1540
1445
|
inputIndex: number,
|
|
1541
1446
|
input: PsbtInput,
|
|
1542
|
-
script:
|
|
1447
|
+
script: Script,
|
|
1543
1448
|
isSegwit: boolean,
|
|
1544
1449
|
isP2SH: boolean,
|
|
1545
1450
|
isP2WSH: boolean,
|
|
1546
1451
|
canRunChecks: boolean = true,
|
|
1547
|
-
solution?:
|
|
1452
|
+
solution?: Uint8Array[],
|
|
1548
1453
|
): {
|
|
1549
|
-
finalScriptSig:
|
|
1550
|
-
finalScriptWitness:
|
|
1454
|
+
finalScriptSig: Script | undefined;
|
|
1455
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
1551
1456
|
} {
|
|
1552
1457
|
const scriptType = classifyScript(script);
|
|
1553
1458
|
if (!canFinalize(input, script, scriptType) && canRunChecks) {
|
|
1554
1459
|
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
1555
1460
|
}
|
|
1556
1461
|
|
|
1462
|
+
if (!input.partialSig) throw new Error('Input missing partial signatures');
|
|
1557
1463
|
return prepareFinalScripts(
|
|
1558
1464
|
script,
|
|
1559
1465
|
scriptType,
|
|
1560
|
-
input.partialSig
|
|
1466
|
+
input.partialSig,
|
|
1561
1467
|
isSegwit,
|
|
1562
1468
|
isP2SH,
|
|
1563
1469
|
isP2WSH,
|
|
@@ -1566,19 +1472,19 @@ export function getFinalScripts(
|
|
|
1566
1472
|
}
|
|
1567
1473
|
|
|
1568
1474
|
export function prepareFinalScripts(
|
|
1569
|
-
script:
|
|
1475
|
+
script: Uint8Array,
|
|
1570
1476
|
scriptType: string,
|
|
1571
1477
|
partialSig: PartialSig[],
|
|
1572
1478
|
isSegwit: boolean,
|
|
1573
1479
|
isP2SH: boolean,
|
|
1574
1480
|
isP2WSH: boolean,
|
|
1575
|
-
solution?:
|
|
1481
|
+
solution?: Uint8Array[],
|
|
1576
1482
|
): {
|
|
1577
|
-
finalScriptSig:
|
|
1578
|
-
finalScriptWitness:
|
|
1483
|
+
finalScriptSig: Script | undefined;
|
|
1484
|
+
finalScriptWitness: Uint8Array | undefined;
|
|
1579
1485
|
} {
|
|
1580
|
-
let finalScriptSig:
|
|
1581
|
-
let finalScriptWitness:
|
|
1486
|
+
let finalScriptSig: Script | undefined;
|
|
1487
|
+
let finalScriptWitness: Uint8Array | undefined;
|
|
1582
1488
|
|
|
1583
1489
|
// Wow, the payments API is very handy
|
|
1584
1490
|
const payment: payments.Payment = getPayment(script, scriptType, partialSig);
|
|
@@ -1586,26 +1492,27 @@ export function prepareFinalScripts(
|
|
|
1586
1492
|
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment } as P2SHPayment);
|
|
1587
1493
|
|
|
1588
1494
|
if (isSegwit) {
|
|
1589
|
-
if (p2wsh) {
|
|
1590
|
-
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness
|
|
1591
|
-
} else if (payment) {
|
|
1592
|
-
finalScriptWitness = witnessStackToScriptWitness(payment.witness
|
|
1495
|
+
if (p2wsh && p2wsh.witness) {
|
|
1496
|
+
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
|
|
1497
|
+
} else if (payment && payment.witness) {
|
|
1498
|
+
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
1593
1499
|
} else {
|
|
1594
1500
|
// nonstandard segwit script
|
|
1595
|
-
finalScriptWitness = witnessStackToScriptWitness(solution ?? [
|
|
1501
|
+
finalScriptWitness = witnessStackToScriptWitness(solution ?? [new Uint8Array([0x00])]);
|
|
1596
1502
|
}
|
|
1597
1503
|
if (p2sh) {
|
|
1598
|
-
finalScriptSig = p2sh?.input;
|
|
1504
|
+
finalScriptSig = p2sh?.input as Script | undefined;
|
|
1599
1505
|
}
|
|
1600
1506
|
} else {
|
|
1601
1507
|
if (p2sh) {
|
|
1602
|
-
finalScriptSig = p2sh?.input;
|
|
1508
|
+
finalScriptSig = p2sh?.input as Script | undefined;
|
|
1603
1509
|
} else {
|
|
1604
1510
|
if (!payment) {
|
|
1605
|
-
finalScriptSig =
|
|
1606
|
-
Array.isArray(solution) && solution[0] ? solution[0] :
|
|
1511
|
+
finalScriptSig = (
|
|
1512
|
+
Array.isArray(solution) && solution[0] ? solution[0] : new Uint8Array([0x01])
|
|
1513
|
+
) as Script;
|
|
1607
1514
|
} else {
|
|
1608
|
-
finalScriptSig = payment.input;
|
|
1515
|
+
finalScriptSig = payment.input as Script | undefined;
|
|
1609
1516
|
}
|
|
1610
1517
|
}
|
|
1611
1518
|
}
|
|
@@ -1618,11 +1525,11 @@ export function prepareFinalScripts(
|
|
|
1618
1525
|
function getHashAndSighashType(
|
|
1619
1526
|
inputs: PsbtInput[],
|
|
1620
1527
|
inputIndex: number,
|
|
1621
|
-
pubkey:
|
|
1528
|
+
pubkey: Uint8Array,
|
|
1622
1529
|
cache: PsbtCache,
|
|
1623
1530
|
sighashTypes: number[],
|
|
1624
1531
|
): {
|
|
1625
|
-
hash:
|
|
1532
|
+
hash: Bytes32;
|
|
1626
1533
|
sighashType: number;
|
|
1627
1534
|
} {
|
|
1628
1535
|
const input = checkForInput(inputs, inputIndex);
|
|
@@ -1634,7 +1541,7 @@ function getHashAndSighashType(
|
|
|
1634
1541
|
sighashTypes,
|
|
1635
1542
|
);
|
|
1636
1543
|
|
|
1637
|
-
checkScriptForPubkey(pubkey, script, 'sign');
|
|
1544
|
+
checkScriptForPubkey(pubkey as PublicKey, script, 'sign');
|
|
1638
1545
|
return {
|
|
1639
1546
|
hash,
|
|
1640
1547
|
sighashType,
|
|
@@ -1648,34 +1555,37 @@ function getHashForSig(
|
|
|
1648
1555
|
forValidate: boolean,
|
|
1649
1556
|
sighashTypes?: number[],
|
|
1650
1557
|
): {
|
|
1651
|
-
script:
|
|
1652
|
-
hash:
|
|
1558
|
+
script: Script;
|
|
1559
|
+
hash: Bytes32;
|
|
1653
1560
|
sighashType: number;
|
|
1654
1561
|
} {
|
|
1655
|
-
const unsignedTx = cache.
|
|
1562
|
+
const unsignedTx = cache.tx;
|
|
1656
1563
|
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
|
1657
1564
|
checkSighashTypeAllowed(sighashType, sighashTypes);
|
|
1658
1565
|
|
|
1659
|
-
let hash:
|
|
1566
|
+
let hash: Bytes32;
|
|
1660
1567
|
let prevout: Output;
|
|
1661
1568
|
|
|
1662
1569
|
if (input.nonWitnessUtxo) {
|
|
1663
1570
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1664
1571
|
|
|
1665
|
-
const prevoutHash = unsignedTx.ins[inputIndex]
|
|
1572
|
+
const prevoutHash = unsignedTx.ins[inputIndex]!.hash;
|
|
1666
1573
|
const utxoHash = nonWitnessUtxoTx.getHash();
|
|
1667
1574
|
|
|
1668
1575
|
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
|
|
1669
|
-
if (!
|
|
1576
|
+
if (!equals(prevoutHash, utxoHash)) {
|
|
1670
1577
|
throw new Error(
|
|
1671
1578
|
`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`,
|
|
1672
1579
|
);
|
|
1673
1580
|
}
|
|
1674
1581
|
|
|
1675
|
-
const prevoutIndex = unsignedTx.ins[inputIndex]
|
|
1676
|
-
prevout = nonWitnessUtxoTx.outs[prevoutIndex]
|
|
1582
|
+
const prevoutIndex = unsignedTx.ins[inputIndex]!.index;
|
|
1583
|
+
prevout = nonWitnessUtxoTx.outs[prevoutIndex]!;
|
|
1677
1584
|
} else if (input.witnessUtxo) {
|
|
1678
|
-
prevout =
|
|
1585
|
+
prevout = {
|
|
1586
|
+
script: input.witnessUtxo.script as Script,
|
|
1587
|
+
value: input.witnessUtxo.value as Satoshi,
|
|
1588
|
+
};
|
|
1679
1589
|
} else {
|
|
1680
1590
|
throw new Error('Need a Utxo input item for signing');
|
|
1681
1591
|
}
|
|
@@ -1688,27 +1598,30 @@ function getHashForSig(
|
|
|
1688
1598
|
input.witnessScript,
|
|
1689
1599
|
);
|
|
1690
1600
|
|
|
1601
|
+
const script = meaningfulScript as Script;
|
|
1602
|
+
|
|
1691
1603
|
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
|
|
1604
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, script, prevout.value, sighashType);
|
|
1605
|
+
} else if (isP2WPKH(meaningfulScript)) {
|
|
1606
|
+
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1607
|
+
const p2pkhPayment = payments.p2pkh({
|
|
1608
|
+
hash: meaningfulScript.subarray(2) as Bytes20,
|
|
1609
|
+
});
|
|
1610
|
+
if (!p2pkhPayment.output) throw new Error('Unable to create signing script');
|
|
1692
1611
|
hash = unsignedTx.hashForWitnessV0(
|
|
1693
1612
|
inputIndex,
|
|
1694
|
-
|
|
1613
|
+
p2pkhPayment.output as Script,
|
|
1695
1614
|
prevout.value,
|
|
1696
1615
|
sighashType,
|
|
1697
1616
|
);
|
|
1698
|
-
} else if (isP2WPKH(meaningfulScript)) {
|
|
1699
|
-
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1700
|
-
const signingScript = payments.p2pkh({
|
|
1701
|
-
hash: meaningfulScript.subarray(2),
|
|
1702
|
-
}).output!;
|
|
1703
|
-
hash = unsignedTx.hashForWitnessV0(inputIndex, signingScript, prevout.value, sighashType);
|
|
1704
1617
|
} else {
|
|
1705
1618
|
// non-segwit
|
|
1706
|
-
if (input.nonWitnessUtxo === undefined && !cache.
|
|
1619
|
+
if (input.nonWitnessUtxo === undefined && !cache.unsafeSignNonSegwit)
|
|
1707
1620
|
throw new Error(
|
|
1708
1621
|
`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
|
1709
|
-
meaningfulScript
|
|
1622
|
+
toHex(meaningfulScript),
|
|
1710
1623
|
);
|
|
1711
|
-
if (!forValidate && cache.
|
|
1624
|
+
if (!forValidate && cache.unsafeSignNonSegwit)
|
|
1712
1625
|
console.warn(
|
|
1713
1626
|
'Warning: Signing non-segwit inputs without the full parent transaction ' +
|
|
1714
1627
|
'means there is a chance that a miner could feed you incorrect information ' +
|
|
@@ -1718,11 +1631,11 @@ function getHashForSig(
|
|
|
1718
1631
|
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
|
|
1719
1632
|
'*********************',
|
|
1720
1633
|
);
|
|
1721
|
-
hash = unsignedTx.hashForSignature(inputIndex,
|
|
1634
|
+
hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
|
|
1722
1635
|
}
|
|
1723
1636
|
|
|
1724
1637
|
return {
|
|
1725
|
-
script
|
|
1638
|
+
script,
|
|
1726
1639
|
sighashType,
|
|
1727
1640
|
hash,
|
|
1728
1641
|
};
|
|
@@ -1733,8 +1646,8 @@ function getAllTaprootHashesForSig(
|
|
|
1733
1646
|
input: PsbtInput,
|
|
1734
1647
|
inputs: PsbtInput[],
|
|
1735
1648
|
cache: PsbtCache,
|
|
1736
|
-
): { pubkey:
|
|
1737
|
-
const allPublicKeys:
|
|
1649
|
+
): { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] {
|
|
1650
|
+
const allPublicKeys: Uint8Array[] = [];
|
|
1738
1651
|
if (input.tapInternalKey) {
|
|
1739
1652
|
const key = getPrevoutTaprootKey(inputIndex, input, cache);
|
|
1740
1653
|
if (key) {
|
|
@@ -1758,46 +1671,58 @@ function getPrevoutTaprootKey(
|
|
|
1758
1671
|
inputIndex: number,
|
|
1759
1672
|
input: PsbtInput,
|
|
1760
1673
|
cache: PsbtCache,
|
|
1761
|
-
):
|
|
1674
|
+
): XOnlyPublicKey | null {
|
|
1762
1675
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
1763
|
-
return isP2TR(script) ?
|
|
1676
|
+
return isP2TR(script) ? (script.subarray(2, 34) as XOnlyPublicKey) : null;
|
|
1764
1677
|
}
|
|
1765
1678
|
|
|
1766
|
-
function trimTaprootSig(signature:
|
|
1767
|
-
return signature.length === 64 ? signature :
|
|
1679
|
+
function trimTaprootSig(signature: Uint8Array): Uint8Array {
|
|
1680
|
+
return signature.length === 64 ? signature : signature.subarray(0, 64);
|
|
1768
1681
|
}
|
|
1769
1682
|
|
|
1770
1683
|
function getTaprootHashesForSig(
|
|
1771
1684
|
inputIndex: number,
|
|
1772
1685
|
input: PsbtInput,
|
|
1773
1686
|
inputs: PsbtInput[],
|
|
1774
|
-
pubkey:
|
|
1687
|
+
pubkey: Uint8Array,
|
|
1775
1688
|
cache: PsbtCache,
|
|
1776
|
-
tapLeafHashToSign?:
|
|
1689
|
+
tapLeafHashToSign?: Uint8Array,
|
|
1777
1690
|
allowedSighashTypes?: number[],
|
|
1778
|
-
): { pubkey:
|
|
1779
|
-
const unsignedTx = cache.
|
|
1691
|
+
): { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] {
|
|
1692
|
+
const unsignedTx = cache.tx;
|
|
1780
1693
|
|
|
1781
1694
|
const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
|
|
1782
1695
|
checkSighashTypeAllowed(sighashType, allowedSighashTypes);
|
|
1783
1696
|
|
|
1784
|
-
|
|
1785
|
-
getScriptAndAmountFromUtxo(index, i, cache)
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1697
|
+
if (!cache.prevOuts) {
|
|
1698
|
+
const prevOuts = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache));
|
|
1699
|
+
cache.prevOuts = prevOuts;
|
|
1700
|
+
cache.signingScripts = prevOuts.map((o) => o.script);
|
|
1701
|
+
cache.values = prevOuts.map((o) => o.value);
|
|
1702
|
+
}
|
|
1703
|
+
const signingScripts = cache.signingScripts as readonly Script[];
|
|
1704
|
+
const values = cache.values as readonly Satoshi[];
|
|
1705
|
+
|
|
1706
|
+
// Compute taproot hash cache once for all inputs (O(n) -> O(1) per input)
|
|
1707
|
+
if (!cache.taprootHashCache) {
|
|
1708
|
+
cache.taprootHashCache = unsignedTx.getTaprootHashCache(signingScripts, values);
|
|
1709
|
+
}
|
|
1710
|
+
const taprootCache = cache.taprootHashCache;
|
|
1789
1711
|
|
|
1790
|
-
const hashes: { pubkey:
|
|
1712
|
+
const hashes: { pubkey: PublicKey; hash: Bytes32; leafHash?: Bytes32 }[] = [];
|
|
1791
1713
|
if (input.tapInternalKey && !tapLeafHashToSign) {
|
|
1792
|
-
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) ||
|
|
1793
|
-
if (toXOnly(pubkey)
|
|
1714
|
+
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || new Uint8Array(0);
|
|
1715
|
+
if (equals(toXOnly(pubkey as PublicKey), outputKey)) {
|
|
1794
1716
|
const tapKeyHash = unsignedTx.hashForWitnessV1(
|
|
1795
1717
|
inputIndex,
|
|
1796
1718
|
signingScripts,
|
|
1797
1719
|
values,
|
|
1798
1720
|
sighashType,
|
|
1721
|
+
undefined,
|
|
1722
|
+
undefined,
|
|
1723
|
+
taprootCache,
|
|
1799
1724
|
);
|
|
1800
|
-
hashes.push({ pubkey, hash: tapKeyHash });
|
|
1725
|
+
hashes.push({ pubkey: pubkey as PublicKey, hash: tapKeyHash });
|
|
1801
1726
|
}
|
|
1802
1727
|
}
|
|
1803
1728
|
|
|
@@ -1810,20 +1735,22 @@ function getTaprootHashesForSig(
|
|
|
1810
1735
|
});
|
|
1811
1736
|
return Object.assign({ hash }, tapLeaf);
|
|
1812
1737
|
})
|
|
1813
|
-
.filter((tapLeaf) => !tapLeafHashToSign ||
|
|
1738
|
+
.filter((tapLeaf) => !tapLeafHashToSign || equals(tapLeafHashToSign, tapLeaf.hash))
|
|
1814
1739
|
.map((tapLeaf) => {
|
|
1815
1740
|
const tapScriptHash = unsignedTx.hashForWitnessV1(
|
|
1816
1741
|
inputIndex,
|
|
1817
1742
|
signingScripts,
|
|
1818
1743
|
values,
|
|
1819
1744
|
sighashType,
|
|
1820
|
-
tapLeaf.hash,
|
|
1745
|
+
tapLeaf.hash as Bytes32,
|
|
1746
|
+
undefined,
|
|
1747
|
+
taprootCache,
|
|
1821
1748
|
);
|
|
1822
1749
|
|
|
1823
1750
|
return {
|
|
1824
|
-
pubkey,
|
|
1751
|
+
pubkey: pubkey as PublicKey,
|
|
1825
1752
|
hash: tapScriptHash,
|
|
1826
|
-
leafHash: tapLeaf.hash,
|
|
1753
|
+
leafHash: tapLeaf.hash as Bytes32,
|
|
1827
1754
|
};
|
|
1828
1755
|
});
|
|
1829
1756
|
|
|
@@ -1841,49 +1768,39 @@ function checkSighashTypeAllowed(sighashType: number, sighashTypes?: number[]):
|
|
|
1841
1768
|
}
|
|
1842
1769
|
|
|
1843
1770
|
function getPayment(
|
|
1844
|
-
script:
|
|
1771
|
+
script: Uint8Array,
|
|
1845
1772
|
scriptType: string,
|
|
1846
1773
|
partialSig: PartialSig[],
|
|
1847
1774
|
): payments.Payment {
|
|
1848
|
-
|
|
1775
|
+
const scriptBranded = script as Script;
|
|
1849
1776
|
switch (scriptType) {
|
|
1850
1777
|
case 'multisig': {
|
|
1851
1778
|
const sigs = getSortedSigs(script, partialSig);
|
|
1852
|
-
|
|
1853
|
-
output:
|
|
1854
|
-
signatures: sigs,
|
|
1779
|
+
return payments.p2ms({
|
|
1780
|
+
output: scriptBranded,
|
|
1781
|
+
signatures: sigs as Signature[],
|
|
1855
1782
|
});
|
|
1856
|
-
break;
|
|
1857
1783
|
}
|
|
1858
1784
|
case 'pubkey':
|
|
1859
|
-
|
|
1860
|
-
output:
|
|
1861
|
-
signature: partialSig[0]
|
|
1785
|
+
return payments.p2pk({
|
|
1786
|
+
output: scriptBranded,
|
|
1787
|
+
signature: partialSig[0]!.signature as Signature,
|
|
1862
1788
|
});
|
|
1863
|
-
break;
|
|
1864
1789
|
case 'pubkeyhash':
|
|
1865
|
-
|
|
1866
|
-
output:
|
|
1867
|
-
pubkey: partialSig[0]
|
|
1868
|
-
signature: partialSig[0]
|
|
1790
|
+
return payments.p2pkh({
|
|
1791
|
+
output: scriptBranded,
|
|
1792
|
+
pubkey: partialSig[0]!.pubkey as PublicKey,
|
|
1793
|
+
signature: partialSig[0]!.signature as Signature,
|
|
1869
1794
|
});
|
|
1870
|
-
break;
|
|
1871
1795
|
case 'witnesspubkeyhash':
|
|
1872
|
-
|
|
1873
|
-
output:
|
|
1874
|
-
pubkey: partialSig[0]
|
|
1875
|
-
signature: partialSig[0]
|
|
1796
|
+
return payments.p2wpkh({
|
|
1797
|
+
output: scriptBranded,
|
|
1798
|
+
pubkey: partialSig[0]!.pubkey as PublicKey,
|
|
1799
|
+
signature: partialSig[0]!.signature as Signature,
|
|
1876
1800
|
});
|
|
1877
|
-
|
|
1801
|
+
default:
|
|
1802
|
+
throw new Error(`Unknown script type: ${scriptType}`);
|
|
1878
1803
|
}
|
|
1879
|
-
return payment!;
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
interface GetScriptReturn {
|
|
1883
|
-
script: Buffer | null;
|
|
1884
|
-
isSegwit: boolean;
|
|
1885
|
-
isP2SH: boolean;
|
|
1886
|
-
isP2WSH: boolean;
|
|
1887
1804
|
}
|
|
1888
1805
|
|
|
1889
1806
|
function getScriptFromInput(
|
|
@@ -1891,7 +1808,7 @@ function getScriptFromInput(
|
|
|
1891
1808
|
input: PsbtInput,
|
|
1892
1809
|
cache: PsbtCache,
|
|
1893
1810
|
): GetScriptReturn {
|
|
1894
|
-
const unsignedTx = cache.
|
|
1811
|
+
const unsignedTx = cache.tx;
|
|
1895
1812
|
const res: GetScriptReturn = {
|
|
1896
1813
|
script: null,
|
|
1897
1814
|
isSegwit: false,
|
|
@@ -1901,20 +1818,20 @@ function getScriptFromInput(
|
|
|
1901
1818
|
res.isP2SH = !!input.redeemScript;
|
|
1902
1819
|
res.isP2WSH = !!input.witnessScript;
|
|
1903
1820
|
if (input.witnessScript) {
|
|
1904
|
-
res.script = input.witnessScript;
|
|
1821
|
+
res.script = input.witnessScript as Script;
|
|
1905
1822
|
} else if (input.redeemScript) {
|
|
1906
|
-
res.script = input.redeemScript;
|
|
1823
|
+
res.script = input.redeemScript as Script;
|
|
1907
1824
|
} else {
|
|
1908
1825
|
if (input.nonWitnessUtxo) {
|
|
1909
1826
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1910
|
-
const prevoutIndex = unsignedTx.ins[inputIndex]
|
|
1911
|
-
res.script = nonWitnessUtxoTx.outs[prevoutIndex]
|
|
1827
|
+
const prevoutIndex = unsignedTx.ins[inputIndex]!.index;
|
|
1828
|
+
res.script = nonWitnessUtxoTx.outs[prevoutIndex]!.script;
|
|
1912
1829
|
} else if (input.witnessUtxo) {
|
|
1913
|
-
res.script = input.witnessUtxo.script;
|
|
1830
|
+
res.script = input.witnessUtxo.script as Script;
|
|
1914
1831
|
}
|
|
1915
1832
|
}
|
|
1916
1833
|
|
|
1917
|
-
if (input.witnessScript || isP2WPKH(res.script
|
|
1834
|
+
if (input.witnessScript || (res.script && isP2WPKH(res.script))) {
|
|
1918
1835
|
res.isSegwit = true;
|
|
1919
1836
|
} else {
|
|
1920
1837
|
try {
|
|
@@ -1928,18 +1845,18 @@ function getScriptFromInput(
|
|
|
1928
1845
|
return res;
|
|
1929
1846
|
}
|
|
1930
1847
|
|
|
1931
|
-
function getSignersFromHD(
|
|
1848
|
+
function getSignersFromHD<T extends HDSigner | HDSignerAsync>(
|
|
1932
1849
|
inputIndex: number,
|
|
1933
1850
|
inputs: PsbtInput[],
|
|
1934
|
-
hdKeyPair:
|
|
1935
|
-
):
|
|
1851
|
+
hdKeyPair: T,
|
|
1852
|
+
): T[] {
|
|
1936
1853
|
const input = checkForInput(inputs, inputIndex);
|
|
1937
1854
|
if (!input.bip32Derivation || input.bip32Derivation.length === 0) {
|
|
1938
1855
|
throw new Error('Need bip32Derivation to sign with HD');
|
|
1939
1856
|
}
|
|
1940
1857
|
const myDerivations = input.bip32Derivation
|
|
1941
1858
|
.map((bipDv) => {
|
|
1942
|
-
if (bipDv.masterFingerprint
|
|
1859
|
+
if (equals(bipDv.masterFingerprint, hdKeyPair.fingerprint)) {
|
|
1943
1860
|
return bipDv;
|
|
1944
1861
|
} else {
|
|
1945
1862
|
return;
|
|
@@ -1953,98 +1870,60 @@ function getSignersFromHD(
|
|
|
1953
1870
|
}
|
|
1954
1871
|
|
|
1955
1872
|
return myDerivations.map((bipDv) => {
|
|
1956
|
-
const node = hdKeyPair.derivePath(bipDv.path);
|
|
1957
|
-
if (!bipDv.pubkey
|
|
1873
|
+
const node = hdKeyPair.derivePath(bipDv.path) as T;
|
|
1874
|
+
if (!equals(bipDv.pubkey, node.publicKey)) {
|
|
1958
1875
|
throw new Error('pubkey did not match bip32Derivation');
|
|
1959
1876
|
}
|
|
1960
1877
|
return node;
|
|
1961
1878
|
});
|
|
1962
1879
|
}
|
|
1963
1880
|
|
|
1964
|
-
function getSortedSigs(script:
|
|
1965
|
-
const p2ms = payments.p2ms({ output: script });
|
|
1881
|
+
function getSortedSigs(script: Uint8Array, partialSig: PartialSig[]): Uint8Array[] {
|
|
1882
|
+
const p2ms = payments.p2ms({ output: script as Script });
|
|
1883
|
+
if (!p2ms.pubkeys) throw new Error('Cannot extract pubkeys from multisig script');
|
|
1966
1884
|
// for each pubkey in order of p2ms script
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
// this last filter removes all the undefined items in the array.
|
|
1977
|
-
})
|
|
1978
|
-
.filter((v) => !!v);
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
|
|
1982
|
-
let offset = 0;
|
|
1983
|
-
|
|
1984
|
-
function readSlice(n: number): Buffer {
|
|
1985
|
-
offset += n;
|
|
1986
|
-
return buffer.subarray(offset - n, offset);
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
function readVarInt(): number {
|
|
1990
|
-
const vi = varuintDecode(buffer, offset);
|
|
1991
|
-
offset += varuintDecode.bytes;
|
|
1992
|
-
return vi;
|
|
1993
|
-
}
|
|
1994
|
-
|
|
1995
|
-
function readVarSlice(): Buffer {
|
|
1996
|
-
return readSlice(readVarInt());
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
function readVector(): Buffer[] {
|
|
2000
|
-
const count = readVarInt();
|
|
2001
|
-
const vector: Buffer[] = [];
|
|
2002
|
-
for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
|
2003
|
-
return vector;
|
|
1885
|
+
const result: Uint8Array[] = [];
|
|
1886
|
+
for (const pk of p2ms.pubkeys) {
|
|
1887
|
+
// filter partialSig array by pubkey being equal
|
|
1888
|
+
const matched = partialSig.filter((ps) => {
|
|
1889
|
+
return equals(ps.pubkey, pk);
|
|
1890
|
+
})[0];
|
|
1891
|
+
if (matched) {
|
|
1892
|
+
result.push(new Uint8Array(matched.signature));
|
|
1893
|
+
}
|
|
2004
1894
|
}
|
|
2005
|
-
|
|
2006
|
-
return readVector();
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
function sighashTypeToString(sighashType: number): string {
|
|
2010
|
-
let text = sighashType & Transaction.SIGHASH_ANYONECANPAY ? 'SIGHASH_ANYONECANPAY | ' : '';
|
|
2011
|
-
const sigMod = sighashType & 0x1f;
|
|
2012
|
-
switch (sigMod) {
|
|
2013
|
-
case Transaction.SIGHASH_ALL:
|
|
2014
|
-
text += 'SIGHASH_ALL';
|
|
2015
|
-
break;
|
|
2016
|
-
case Transaction.SIGHASH_SINGLE:
|
|
2017
|
-
text += 'SIGHASH_SINGLE';
|
|
2018
|
-
break;
|
|
2019
|
-
case Transaction.SIGHASH_NONE:
|
|
2020
|
-
text += 'SIGHASH_NONE';
|
|
2021
|
-
break;
|
|
2022
|
-
}
|
|
2023
|
-
return text;
|
|
1895
|
+
return result;
|
|
2024
1896
|
}
|
|
2025
1897
|
|
|
2026
1898
|
function addNonWitnessTxCache(cache: PsbtCache, input: PsbtInput, inputIndex: number): void {
|
|
2027
|
-
|
|
2028
|
-
|
|
1899
|
+
if (!input.nonWitnessUtxo) throw new Error('nonWitnessUtxo is required');
|
|
1900
|
+
// Prevent prototype pollution - ensure input is a valid object
|
|
1901
|
+
if (input === null || input === Object.prototype) {
|
|
1902
|
+
throw new Error('Invalid input object');
|
|
1903
|
+
}
|
|
1904
|
+
const nonWitnessUtxoBuf = input.nonWitnessUtxo;
|
|
1905
|
+
cache.nonWitnessUtxoBufCache[inputIndex] = nonWitnessUtxoBuf;
|
|
1906
|
+
cache.nonWitnessUtxoTxCache[inputIndex] = Transaction.fromBuffer(nonWitnessUtxoBuf);
|
|
2029
1907
|
|
|
2030
1908
|
const self = cache;
|
|
2031
1909
|
const selfIndex = inputIndex;
|
|
2032
1910
|
delete input.nonWitnessUtxo;
|
|
2033
|
-
|
|
1911
|
+
// Using Reflect.defineProperty to avoid prototype pollution concerns
|
|
1912
|
+
Reflect.defineProperty(input, 'nonWitnessUtxo', {
|
|
2034
1913
|
enumerable: true,
|
|
2035
|
-
get():
|
|
2036
|
-
const buf = self.
|
|
2037
|
-
const txCache = self.
|
|
1914
|
+
get(): Uint8Array {
|
|
1915
|
+
const buf = self.nonWitnessUtxoBufCache[selfIndex];
|
|
1916
|
+
const txCache = self.nonWitnessUtxoTxCache[selfIndex];
|
|
2038
1917
|
if (buf !== undefined) {
|
|
2039
1918
|
return buf;
|
|
2040
1919
|
} else {
|
|
2041
|
-
const newBuf = txCache
|
|
2042
|
-
self.
|
|
1920
|
+
const newBuf = txCache!.toBuffer();
|
|
1921
|
+
self.nonWitnessUtxoBufCache[selfIndex] = newBuf;
|
|
2043
1922
|
return newBuf;
|
|
2044
1923
|
}
|
|
2045
1924
|
},
|
|
2046
|
-
set(data:
|
|
2047
|
-
self.
|
|
1925
|
+
set(data: Uint8Array): void {
|
|
1926
|
+
self.nonWitnessUtxoBufCache[selfIndex] = data;
|
|
2048
1927
|
},
|
|
2049
1928
|
});
|
|
2050
1929
|
}
|
|
@@ -2056,34 +1935,35 @@ function inputFinalizeGetAmts(
|
|
|
2056
1935
|
mustFinalize: boolean,
|
|
2057
1936
|
disableOutputChecks?: boolean,
|
|
2058
1937
|
): void {
|
|
2059
|
-
let inputAmount =
|
|
1938
|
+
let inputAmount = 0n;
|
|
2060
1939
|
inputs.forEach((input, idx) => {
|
|
2061
|
-
if (mustFinalize && input.finalScriptSig)
|
|
1940
|
+
if (mustFinalize && input.finalScriptSig)
|
|
1941
|
+
tx.ins[idx]!.script = input.finalScriptSig as Script;
|
|
2062
1942
|
if (mustFinalize && input.finalScriptWitness) {
|
|
2063
|
-
tx.ins[idx]
|
|
1943
|
+
tx.ins[idx]!.witness = scriptWitnessToWitnessStack(input.finalScriptWitness);
|
|
2064
1944
|
}
|
|
2065
1945
|
if (input.witnessUtxo) {
|
|
2066
1946
|
inputAmount += input.witnessUtxo.value;
|
|
2067
1947
|
} else if (input.nonWitnessUtxo) {
|
|
2068
1948
|
const nwTx = nonWitnessUtxoTxFromCache(cache, input, idx);
|
|
2069
|
-
const vout = tx.ins[idx]
|
|
2070
|
-
const out = nwTx.outs[vout]
|
|
1949
|
+
const vout = tx.ins[idx]!.index;
|
|
1950
|
+
const out = nwTx.outs[vout]!;
|
|
2071
1951
|
inputAmount += out.value;
|
|
2072
1952
|
}
|
|
2073
1953
|
});
|
|
2074
|
-
const outputAmount = tx.outs.reduce((total, o) => total + o.value,
|
|
1954
|
+
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
|
|
2075
1955
|
const fee = inputAmount - outputAmount;
|
|
2076
1956
|
if (!disableOutputChecks) {
|
|
2077
|
-
if (fee <
|
|
1957
|
+
if (fee < 0n) {
|
|
2078
1958
|
throw new Error(
|
|
2079
1959
|
`Outputs are spending more than Inputs ${inputAmount} < ${outputAmount}`,
|
|
2080
1960
|
);
|
|
2081
1961
|
}
|
|
2082
1962
|
}
|
|
2083
1963
|
const bytes = tx.virtualSize();
|
|
2084
|
-
cache.
|
|
2085
|
-
cache.
|
|
2086
|
-
cache.
|
|
1964
|
+
cache.fee = Number(fee);
|
|
1965
|
+
cache.extractedTx = tx;
|
|
1966
|
+
cache.feeRate = Math.floor(Number(fee) / bytes);
|
|
2087
1967
|
}
|
|
2088
1968
|
|
|
2089
1969
|
function nonWitnessUtxoTxFromCache(
|
|
@@ -2091,14 +1971,14 @@ function nonWitnessUtxoTxFromCache(
|
|
|
2091
1971
|
input: PsbtInput,
|
|
2092
1972
|
inputIndex: number,
|
|
2093
1973
|
): Transaction {
|
|
2094
|
-
const c = cache.
|
|
1974
|
+
const c = cache.nonWitnessUtxoTxCache;
|
|
2095
1975
|
if (!c[inputIndex]) {
|
|
2096
1976
|
addNonWitnessTxCache(cache, input, inputIndex);
|
|
2097
1977
|
}
|
|
2098
|
-
return c[inputIndex]
|
|
1978
|
+
return c[inputIndex]!;
|
|
2099
1979
|
}
|
|
2100
1980
|
|
|
2101
|
-
function getScriptFromUtxo(inputIndex: number, input: PsbtInput, cache: PsbtCache):
|
|
1981
|
+
function getScriptFromUtxo(inputIndex: number, input: PsbtInput, cache: PsbtCache): Script {
|
|
2102
1982
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
2103
1983
|
return script;
|
|
2104
1984
|
}
|
|
@@ -2107,15 +1987,15 @@ function getScriptAndAmountFromUtxo(
|
|
|
2107
1987
|
inputIndex: number,
|
|
2108
1988
|
input: PsbtInput,
|
|
2109
1989
|
cache: PsbtCache,
|
|
2110
|
-
): { script:
|
|
1990
|
+
): { script: Script; value: Satoshi } {
|
|
2111
1991
|
if (input.witnessUtxo !== undefined) {
|
|
2112
1992
|
return {
|
|
2113
|
-
script: input.witnessUtxo.script,
|
|
2114
|
-
value: input.witnessUtxo.value,
|
|
1993
|
+
script: input.witnessUtxo.script as Script,
|
|
1994
|
+
value: input.witnessUtxo.value as Satoshi,
|
|
2115
1995
|
};
|
|
2116
1996
|
} else if (input.nonWitnessUtxo !== undefined) {
|
|
2117
1997
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
2118
|
-
const o = nonWitnessUtxoTx.outs[cache.
|
|
1998
|
+
const o = nonWitnessUtxoTx.outs[cache.tx.ins[inputIndex]!.index]!;
|
|
2119
1999
|
return { script: o.script, value: o.value };
|
|
2120
2000
|
} else {
|
|
2121
2001
|
throw new Error("Can't find pubkey in input without Utxo data");
|
|
@@ -2123,7 +2003,7 @@ function getScriptAndAmountFromUtxo(
|
|
|
2123
2003
|
}
|
|
2124
2004
|
|
|
2125
2005
|
function pubkeyInInput(
|
|
2126
|
-
pubkey:
|
|
2006
|
+
pubkey: PublicKey,
|
|
2127
2007
|
input: PsbtInput,
|
|
2128
2008
|
inputIndex: number,
|
|
2129
2009
|
cache: PsbtCache,
|
|
@@ -2140,12 +2020,12 @@ function pubkeyInInput(
|
|
|
2140
2020
|
}
|
|
2141
2021
|
|
|
2142
2022
|
function pubkeyInOutput(
|
|
2143
|
-
pubkey:
|
|
2023
|
+
pubkey: PublicKey,
|
|
2144
2024
|
output: PsbtOutput,
|
|
2145
2025
|
outputIndex: number,
|
|
2146
2026
|
cache: PsbtCache,
|
|
2147
2027
|
): boolean {
|
|
2148
|
-
const script = cache.
|
|
2028
|
+
const script = cache.tx.outs[outputIndex]!.script;
|
|
2149
2029
|
const { meaningfulScript } = getMeaningfulScript(
|
|
2150
2030
|
script,
|
|
2151
2031
|
outputIndex,
|
|
@@ -2156,122 +2036,23 @@ function pubkeyInOutput(
|
|
|
2156
2036
|
return pubkeyInScript(pubkey, meaningfulScript);
|
|
2157
2037
|
}
|
|
2158
2038
|
|
|
2159
|
-
function redeemFromFinalScriptSig(finalScript:
|
|
2039
|
+
function redeemFromFinalScriptSig(finalScript: Uint8Array | undefined): Uint8Array | undefined {
|
|
2160
2040
|
if (!finalScript) return;
|
|
2161
2041
|
const decomp = bscript.decompile(finalScript);
|
|
2162
2042
|
if (!decomp) return;
|
|
2163
|
-
const lastItem = decomp[decomp.length - 1]
|
|
2164
|
-
if (!
|
|
2043
|
+
const lastItem = decomp[decomp.length - 1]!;
|
|
2044
|
+
if (!(lastItem instanceof Uint8Array) || isPubkeyLike(lastItem) || isSigLike(lastItem)) return;
|
|
2165
2045
|
const sDecomp = bscript.decompile(lastItem);
|
|
2166
2046
|
if (!sDecomp) return;
|
|
2167
2047
|
return lastItem;
|
|
2168
2048
|
}
|
|
2169
2049
|
|
|
2170
|
-
function redeemFromFinalWitnessScript(finalScript:
|
|
2050
|
+
function redeemFromFinalWitnessScript(finalScript: Uint8Array | undefined): Uint8Array | undefined {
|
|
2171
2051
|
if (!finalScript) return;
|
|
2172
2052
|
const decomp = scriptWitnessToWitnessStack(finalScript);
|
|
2173
|
-
const lastItem = decomp[decomp.length - 1]
|
|
2053
|
+
const lastItem = decomp[decomp.length - 1]!;
|
|
2174
2054
|
if (isPubkeyLike(lastItem)) return;
|
|
2175
2055
|
const sDecomp = bscript.decompile(lastItem);
|
|
2176
2056
|
if (!sDecomp) return;
|
|
2177
2057
|
return lastItem;
|
|
2178
2058
|
}
|
|
2179
|
-
|
|
2180
|
-
function compressPubkey(pubkey: Buffer): Buffer {
|
|
2181
|
-
if (pubkey.length === 65) {
|
|
2182
|
-
const parity = pubkey[64] & 1;
|
|
2183
|
-
const newKey = Buffer.from(pubkey.subarray(0, 33));
|
|
2184
|
-
newKey[0] = 2 | parity;
|
|
2185
|
-
return newKey;
|
|
2186
|
-
}
|
|
2187
|
-
return Buffer.from(pubkey);
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
function isPubkeyLike(buf: Buffer): boolean {
|
|
2191
|
-
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
function isSigLike(buf: Buffer): boolean {
|
|
2195
|
-
return bscript.isCanonicalScriptSignature(buf);
|
|
2196
|
-
}
|
|
2197
|
-
|
|
2198
|
-
function getMeaningfulScript(
|
|
2199
|
-
script: Buffer,
|
|
2200
|
-
index: number,
|
|
2201
|
-
ioType: 'input' | 'output',
|
|
2202
|
-
redeemScript?: Buffer,
|
|
2203
|
-
witnessScript?: Buffer,
|
|
2204
|
-
): {
|
|
2205
|
-
meaningfulScript: Buffer;
|
|
2206
|
-
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
|
|
2207
|
-
} {
|
|
2208
|
-
const isP2SH = isP2SHScript(script);
|
|
2209
|
-
const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript);
|
|
2210
|
-
const isP2WSH = isP2WSHScript(script);
|
|
2211
|
-
|
|
2212
|
-
if (isP2SH && redeemScript === undefined)
|
|
2213
|
-
throw new Error('scriptPubkey is P2SH but redeemScript missing');
|
|
2214
|
-
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
|
|
2215
|
-
throw new Error('scriptPubkey or redeemScript is P2WSH but witnessScript missing');
|
|
2216
|
-
|
|
2217
|
-
let meaningfulScript: Buffer;
|
|
2218
|
-
|
|
2219
|
-
if (isP2SHP2WSH) {
|
|
2220
|
-
meaningfulScript = witnessScript!;
|
|
2221
|
-
checkRedeemScript(index, script, redeemScript, ioType);
|
|
2222
|
-
checkWitnessScript(index, redeemScript, witnessScript!, ioType);
|
|
2223
|
-
checkInvalidP2WSH(meaningfulScript);
|
|
2224
|
-
} else if (isP2WSH) {
|
|
2225
|
-
meaningfulScript = witnessScript!;
|
|
2226
|
-
checkWitnessScript(index, script, witnessScript!, ioType);
|
|
2227
|
-
checkInvalidP2WSH(meaningfulScript);
|
|
2228
|
-
} else if (isP2SH) {
|
|
2229
|
-
meaningfulScript = redeemScript!;
|
|
2230
|
-
checkRedeemScript(index, script, redeemScript!, ioType);
|
|
2231
|
-
} else {
|
|
2232
|
-
meaningfulScript = script;
|
|
2233
|
-
}
|
|
2234
|
-
return {
|
|
2235
|
-
meaningfulScript,
|
|
2236
|
-
type: isP2SHP2WSH ? 'p2sh-p2wsh' : isP2SH ? 'p2sh' : isP2WSH ? 'p2wsh' : 'raw',
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
function checkInvalidP2WSH(script: Buffer): void {
|
|
2241
|
-
if (isP2WPKH(script) || isP2SHScript(script)) {
|
|
2242
|
-
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
|
|
2246
|
-
type AllScriptType =
|
|
2247
|
-
| 'witnesspubkeyhash'
|
|
2248
|
-
| 'pubkeyhash'
|
|
2249
|
-
| 'multisig'
|
|
2250
|
-
| 'pubkey'
|
|
2251
|
-
| 'nonstandard'
|
|
2252
|
-
| 'p2sh-witnesspubkeyhash'
|
|
2253
|
-
| 'p2sh-pubkeyhash'
|
|
2254
|
-
| 'p2sh-multisig'
|
|
2255
|
-
| 'p2sh-pubkey'
|
|
2256
|
-
| 'p2sh-nonstandard'
|
|
2257
|
-
| 'p2wsh-pubkeyhash'
|
|
2258
|
-
| 'p2wsh-multisig'
|
|
2259
|
-
| 'p2wsh-pubkey'
|
|
2260
|
-
| 'p2wsh-nonstandard'
|
|
2261
|
-
| 'p2sh-p2wsh-pubkeyhash'
|
|
2262
|
-
| 'p2sh-p2wsh-multisig'
|
|
2263
|
-
| 'p2sh-p2wsh-pubkey'
|
|
2264
|
-
| 'p2sh-p2wsh-nonstandard';
|
|
2265
|
-
type ScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard';
|
|
2266
|
-
|
|
2267
|
-
function classifyScript(script: Buffer): ScriptType {
|
|
2268
|
-
if (isP2WPKH(script)) return 'witnesspubkeyhash';
|
|
2269
|
-
if (isP2PKH(script)) return 'pubkeyhash';
|
|
2270
|
-
if (isP2MS(script)) return 'multisig';
|
|
2271
|
-
if (isP2PK(script)) return 'pubkey';
|
|
2272
|
-
return 'nonstandard';
|
|
2273
|
-
}
|
|
2274
|
-
|
|
2275
|
-
function range(n: number): number[] {
|
|
2276
|
-
return [...Array(n).keys()];
|
|
2277
|
-
}
|