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