@btc-vision/bitcoin 6.5.6 → 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/HOW_TO_WRITE_GOOD_CODE.md +2436 -0
- package/benchmark/psbt-2000-inputs.bench.ts +178 -0
- package/benchmark/signing.bench.ts +147 -0
- package/browser/address.d.ts +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 +36 -12
- package/build/pubkey.js.map +1 -0
- package/build/push_data.d.ts +24 -2
- package/build/push_data.d.ts.map +1 -0
- package/build/push_data.js +44 -12
- package/build/push_data.js.map +1 -0
- package/build/script.d.ts +33 -8
- package/build/script.d.ts.map +1 -0
- package/build/script.js +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 +66 -8
- 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 +22 -23
- 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 +2 -0
- package/browser/bufferutils.d.ts +0 -34
- package/browser/chunks/crypto-BhCpKpek.js +0 -2033
- package/browser/chunks/payments-B1wlSccx.js +0 -1089
- package/browser/chunks/psbt-BCNk7JUx.js +0 -4055
- package/browser/chunks/script-DyPItFEl.js +0 -318
- package/browser/chunks/transaction-C_UbhMGn.js +0 -432
- package/browser/chunks/utils-DNZi-T5W.js +0 -761
- package/browser/ecc_lib.d.ts +0 -3
- package/browser/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/browser/hooks/HookedSigner.d.ts +0 -4
- package/browser/hooks/SignatureManager.d.ts +0 -13
- package/browser/payments/lazy.d.ts +0 -2
- package/browser/typeforce.d.ts +0 -38
- package/build/bufferutils.d.ts +0 -34
- package/build/bufferutils.js +0 -141
- package/build/ecc_lib.d.ts +0 -3
- package/build/ecc_lib.js +0 -61
- package/build/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/build/hooks/AdvancedSignatureManager.js +0 -52
- package/build/hooks/HookedSigner.d.ts +0 -4
- package/build/hooks/HookedSigner.js +0 -64
- package/build/hooks/SignatureManager.d.ts +0 -13
- package/build/hooks/SignatureManager.js +0 -45
- package/build/payments/lazy.d.ts +0 -2
- package/build/payments/lazy.js +0 -28
- package/build/tsconfig.tsbuildinfo +0 -1
- package/src/bufferutils.ts +0 -188
- package/src/ecc_lib.ts +0 -94
- package/src/hooks/AdvancedSignatureManager.ts +0 -104
- package/src/hooks/HookedSigner.ts +0 -108
- package/src/hooks/SignatureManager.ts +0 -84
- package/src/payments/lazy.ts +0 -28
- package/src/typeforce.d.ts +0 -38
- package/tsconfig.webpack.json +0 -18
package/build/psbt.js
CHANGED
|
@@ -1,94 +1,156 @@
|
|
|
1
|
-
import { Psbt as PsbtBase } from 'bip174';
|
|
2
|
-
import
|
|
3
|
-
const varuintDecode = varuint.decode;
|
|
4
|
-
import { checkForInput, checkForOutput } from 'bip174/src/lib/utils.js';
|
|
1
|
+
import { Psbt as PsbtBase, checkForInput, checkForOutput, } from 'bip174';
|
|
2
|
+
import { clone, reverse, equals, fromHex, toHex, fromBase64 } from './io/index.js';
|
|
5
3
|
import { fromOutputScript, isUnknownSegwitVersion, toOutputScript } from './address.js';
|
|
6
|
-
import { cloneBuffer, reverseBuffer } from './bufferutils.js';
|
|
7
4
|
import { bitcoin as btcNetwork } from './networks.js';
|
|
8
5
|
import * as payments from './payments/index.js';
|
|
9
6
|
import { tapleafHash } from './payments/bip341.js';
|
|
10
|
-
import { checkTaprootInputFields,
|
|
7
|
+
import { checkTaprootInputFields, checkTaprootOutputFields, isTaprootInput, serializeTaprootSignature, tapScriptFinalizer, } from './psbt/bip371.js';
|
|
11
8
|
import { toXOnly } from './pubkey.js';
|
|
12
|
-
import {
|
|
9
|
+
import { isP2TR, isP2WPKH, pubkeyInScript, witnessStackToScriptWitness, } from './psbt/psbtutils.js';
|
|
10
|
+
import { check32Bit, checkCache, checkInputsForPartialSig, checkPartialSigSighashes, checkScriptForPubkey, checkTxEmpty, checkTxForDupeIns, checkTxInputCache, isFinalized, } from './psbt/validation.js';
|
|
11
|
+
import { checkInvalidP2WSH, classifyScript, compressPubkey, getMeaningfulScript, isPubkeyLike, isSigLike, range, scriptWitnessToWitnessStack, sighashTypeToString, } from './psbt/utils.js';
|
|
13
12
|
import * as bscript from './script.js';
|
|
14
13
|
import { Transaction } from './transaction.js';
|
|
14
|
+
/**
|
|
15
|
+
* These are the default arguments for a Psbt instance.
|
|
16
|
+
*/
|
|
15
17
|
const DEFAULT_OPTS = {
|
|
18
|
+
/**
|
|
19
|
+
* A bitcoinjs Network object. This is only used if you pass an `address`
|
|
20
|
+
* parameter to addOutput. Otherwise it is not needed and can be left default.
|
|
21
|
+
*/
|
|
16
22
|
network: btcNetwork,
|
|
17
|
-
|
|
23
|
+
/**
|
|
24
|
+
* When extractTransaction is called, the fee rate is checked.
|
|
25
|
+
* THIS IS NOT TO BE RELIED ON.
|
|
26
|
+
* It is only here as a last ditch effort to prevent sending a 500 BTC fee etc.
|
|
27
|
+
*/
|
|
28
|
+
maximumFeeRate: 5000, // satoshi per byte
|
|
18
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
32
|
+
* There are 6 roles that this class fulfills. (Explained in BIP174)
|
|
33
|
+
*
|
|
34
|
+
* Creator: This can be done with `new Psbt()`
|
|
35
|
+
*
|
|
36
|
+
* Updater: This can be done with `psbt.addInput(input)`, `psbt.addInputs(inputs)`,
|
|
37
|
+
* `psbt.addOutput(output)`, `psbt.addOutputs(outputs)` when you are looking to
|
|
38
|
+
* add new inputs and outputs to the PSBT, and `psbt.updateGlobal(itemObject)`,
|
|
39
|
+
* `psbt.updateInput(itemObject)`, `psbt.updateOutput(itemObject)`
|
|
40
|
+
* addInput requires hash: Uint8Array | string; and index: number; as attributes
|
|
41
|
+
* and can also include any attributes that are used in updateInput method.
|
|
42
|
+
* addOutput requires script: Uint8Array; and value: bigint; and likewise can include
|
|
43
|
+
* data for updateOutput.
|
|
44
|
+
* For a list of what attributes should be what types. Check the bip174 library.
|
|
45
|
+
* Also, check the integration tests for some examples of usage.
|
|
46
|
+
*
|
|
47
|
+
* Signer: There are a few methods. signAllInputs and signAllInputsAsync, which will search all input
|
|
48
|
+
* information for your pubkey or pubkeyhash, and only sign inputs where it finds
|
|
49
|
+
* your info. Or you can explicitly sign a specific input with signInput and
|
|
50
|
+
* signInputAsync. For the async methods you can create a SignerAsync object
|
|
51
|
+
* and use something like a hardware wallet to sign with. (You must implement this)
|
|
52
|
+
*
|
|
53
|
+
* Combiner: psbts can be combined easily with `psbt.combine(psbt2, psbt3, psbt4 ...)`
|
|
54
|
+
* the psbt calling combine will always have precedence when a conflict occurs.
|
|
55
|
+
* Combine checks if the internal bitcoin transaction is the same, so be sure that
|
|
56
|
+
* all sequences, version, locktime, etc. are the same before combining.
|
|
57
|
+
*
|
|
58
|
+
* Input Finalizer: This role is fairly important. Not only does it need to construct
|
|
59
|
+
* the input scriptSigs and witnesses, but it SHOULD verify the signatures etc.
|
|
60
|
+
* Before running `psbt.finalizeAllInputs()` please run `psbt.validateSignaturesOfAllInputs()`
|
|
61
|
+
* Running any finalize method will delete any data in the input(s) that are no longer
|
|
62
|
+
* needed due to the finalized scripts containing the information.
|
|
63
|
+
*
|
|
64
|
+
* Transaction Extractor: This role will perform some checks before returning a
|
|
65
|
+
* Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Psbt class can parse and generate a PSBT binary based off of the BIP174.
|
|
69
|
+
*/
|
|
19
70
|
export class Psbt {
|
|
71
|
+
data;
|
|
72
|
+
#cache;
|
|
73
|
+
#opts;
|
|
20
74
|
constructor(opts = {}, data = new PsbtBase(new PsbtTransaction())) {
|
|
21
75
|
this.data = data;
|
|
22
|
-
this
|
|
23
|
-
this
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
76
|
+
this.#opts = Object.assign({}, DEFAULT_OPTS, opts);
|
|
77
|
+
this.#cache = {
|
|
78
|
+
nonWitnessUtxoTxCache: [],
|
|
79
|
+
nonWitnessUtxoBufCache: [],
|
|
80
|
+
txInCache: {},
|
|
81
|
+
// unsignedTx.tx property is dynamically added by PsbtBase
|
|
82
|
+
tx: this.data.globalMap.unsignedTx.tx,
|
|
83
|
+
unsafeSignNonSegwit: false,
|
|
84
|
+
hasSignatures: false,
|
|
29
85
|
};
|
|
30
86
|
if (opts.version === 3) {
|
|
31
87
|
this.setVersionTRUC();
|
|
32
88
|
}
|
|
33
89
|
else if (this.data.inputs.length === 0)
|
|
34
90
|
this.setVersion(2);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
91
|
+
}
|
|
92
|
+
/** @internal - Exposed for testing. Do not use in production code. */
|
|
93
|
+
get __CACHE() {
|
|
94
|
+
return this.#cache;
|
|
95
|
+
}
|
|
96
|
+
/** @internal - Exposed for testing. Do not use in production code. */
|
|
97
|
+
get opts() {
|
|
98
|
+
return this.#opts;
|
|
43
99
|
}
|
|
44
100
|
get inputCount() {
|
|
45
101
|
return this.data.inputs.length;
|
|
46
102
|
}
|
|
47
103
|
get version() {
|
|
48
|
-
return this.
|
|
104
|
+
return this.#cache.tx.version;
|
|
49
105
|
}
|
|
50
106
|
set version(version) {
|
|
51
107
|
this.setVersion(version);
|
|
52
108
|
}
|
|
53
109
|
get locktime() {
|
|
54
|
-
return this.
|
|
110
|
+
return this.#cache.tx.locktime;
|
|
55
111
|
}
|
|
56
112
|
set locktime(locktime) {
|
|
57
113
|
this.setLocktime(locktime);
|
|
58
114
|
}
|
|
59
115
|
get txInputs() {
|
|
60
|
-
return this.
|
|
61
|
-
hash:
|
|
116
|
+
return this.#cache.tx.ins.map((input) => ({
|
|
117
|
+
hash: clone(input.hash),
|
|
62
118
|
index: input.index,
|
|
63
119
|
sequence: input.sequence,
|
|
64
120
|
}));
|
|
65
121
|
}
|
|
66
122
|
get txOutputs() {
|
|
67
|
-
return this.
|
|
123
|
+
return this.#cache.tx.outs.map((output) => {
|
|
68
124
|
let address;
|
|
69
125
|
try {
|
|
70
|
-
address = fromOutputScript(output.script, this
|
|
126
|
+
address = fromOutputScript(output.script, this.#opts.network);
|
|
71
127
|
}
|
|
72
128
|
catch (_) { }
|
|
73
129
|
return {
|
|
74
|
-
script:
|
|
130
|
+
script: clone(output.script),
|
|
75
131
|
value: output.value,
|
|
76
132
|
address,
|
|
77
133
|
};
|
|
78
134
|
});
|
|
79
135
|
}
|
|
80
136
|
static fromBase64(data, opts = {}) {
|
|
81
|
-
const buffer =
|
|
137
|
+
const buffer = fromBase64(data);
|
|
82
138
|
return this.fromBuffer(buffer, opts);
|
|
83
139
|
}
|
|
84
140
|
static fromHex(data, opts = {}) {
|
|
85
|
-
const buffer =
|
|
141
|
+
const buffer = fromHex(data);
|
|
86
142
|
return this.fromBuffer(buffer, opts);
|
|
87
143
|
}
|
|
88
144
|
static fromBuffer(buffer, opts = {}) {
|
|
89
145
|
const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
|
|
90
146
|
const psbt = new Psbt(opts, psbtBase);
|
|
91
|
-
checkTxForDupeIns(psbt.
|
|
147
|
+
checkTxForDupeIns(psbt.#cache.tx, psbt.#cache);
|
|
148
|
+
// Check if restored PSBT has any signatures (partial or finalized)
|
|
149
|
+
psbt.#cache.hasSignatures = psbt.data.inputs.some((input) => input.partialSig?.length ||
|
|
150
|
+
input.tapKeySig ||
|
|
151
|
+
input.tapScriptSig?.length ||
|
|
152
|
+
input.finalScriptSig ||
|
|
153
|
+
input.finalScriptWitness);
|
|
92
154
|
return psbt;
|
|
93
155
|
}
|
|
94
156
|
combine(...those) {
|
|
@@ -96,19 +158,20 @@ export class Psbt {
|
|
|
96
158
|
return this;
|
|
97
159
|
}
|
|
98
160
|
clone() {
|
|
99
|
-
|
|
100
|
-
|
|
161
|
+
// TODO: more efficient cloning
|
|
162
|
+
const clonedOpts = JSON.parse(JSON.stringify(this.#opts));
|
|
163
|
+
return Psbt.fromBuffer(new Uint8Array(this.data.toBuffer()), clonedOpts);
|
|
101
164
|
}
|
|
102
165
|
setMaximumFeeRate(satoshiPerByte) {
|
|
103
|
-
check32Bit(satoshiPerByte);
|
|
104
|
-
this
|
|
166
|
+
check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
|
|
167
|
+
this.#opts.maximumFeeRate = satoshiPerByte;
|
|
105
168
|
}
|
|
106
169
|
setVersion(version) {
|
|
107
170
|
check32Bit(version);
|
|
108
|
-
checkInputsForPartialSig(this.data.inputs, 'setVersion');
|
|
109
|
-
const c = this
|
|
110
|
-
c.
|
|
111
|
-
c.
|
|
171
|
+
checkInputsForPartialSig(this.data.inputs, 'setVersion', this.#cache.hasSignatures);
|
|
172
|
+
const c = this.#cache;
|
|
173
|
+
c.tx.version = version;
|
|
174
|
+
c.extractedTx = undefined;
|
|
112
175
|
return this;
|
|
113
176
|
}
|
|
114
177
|
setVersionTRUC() {
|
|
@@ -116,21 +179,21 @@ export class Psbt {
|
|
|
116
179
|
}
|
|
117
180
|
setLocktime(locktime) {
|
|
118
181
|
check32Bit(locktime);
|
|
119
|
-
checkInputsForPartialSig(this.data.inputs, 'setLocktime');
|
|
120
|
-
const c = this
|
|
121
|
-
c.
|
|
122
|
-
c.
|
|
182
|
+
checkInputsForPartialSig(this.data.inputs, 'setLocktime', this.#cache.hasSignatures);
|
|
183
|
+
const c = this.#cache;
|
|
184
|
+
c.tx.locktime = locktime;
|
|
185
|
+
c.extractedTx = undefined;
|
|
123
186
|
return this;
|
|
124
187
|
}
|
|
125
188
|
setInputSequence(inputIndex, sequence) {
|
|
126
189
|
check32Bit(sequence);
|
|
127
|
-
checkInputsForPartialSig(this.data.inputs, 'setInputSequence');
|
|
128
|
-
const c = this
|
|
129
|
-
if (c.
|
|
190
|
+
checkInputsForPartialSig(this.data.inputs, 'setInputSequence', this.#cache.hasSignatures);
|
|
191
|
+
const c = this.#cache;
|
|
192
|
+
if (c.tx.ins.length <= inputIndex) {
|
|
130
193
|
throw new Error('Input index too high');
|
|
131
194
|
}
|
|
132
|
-
c.
|
|
133
|
-
c.
|
|
195
|
+
c.tx.ins[inputIndex].sequence = sequence;
|
|
196
|
+
c.extractedTx = undefined;
|
|
134
197
|
return this;
|
|
135
198
|
}
|
|
136
199
|
addInputs(inputDatas, checkPartialSigs = true) {
|
|
@@ -144,51 +207,80 @@ export class Psbt {
|
|
|
144
207
|
}
|
|
145
208
|
checkTaprootInputFields(inputData, inputData, 'addInput');
|
|
146
209
|
if (checkPartialSigs) {
|
|
147
|
-
checkInputsForPartialSig(this.data.inputs, 'addInput');
|
|
210
|
+
checkInputsForPartialSig(this.data.inputs, 'addInput', this.#cache.hasSignatures);
|
|
148
211
|
}
|
|
149
212
|
if (inputData.witnessScript)
|
|
150
213
|
checkInvalidP2WSH(inputData.witnessScript);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
214
|
+
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
215
|
+
const normalizedInputData = inputData.witnessUtxo
|
|
216
|
+
? {
|
|
217
|
+
...inputData,
|
|
218
|
+
witnessUtxo: {
|
|
219
|
+
script: inputData.witnessUtxo.script,
|
|
220
|
+
value: typeof inputData.witnessUtxo.value === 'bigint'
|
|
221
|
+
? inputData.witnessUtxo.value
|
|
222
|
+
: BigInt(inputData.witnessUtxo.value),
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
: inputData;
|
|
226
|
+
const c = this.#cache;
|
|
227
|
+
this.data.addInput(normalizedInputData);
|
|
228
|
+
const txIn = c.tx.ins[c.tx.ins.length - 1];
|
|
154
229
|
checkTxInputCache(c, txIn);
|
|
155
230
|
const inputIndex = this.data.inputs.length - 1;
|
|
156
231
|
const input = this.data.inputs[inputIndex];
|
|
157
232
|
if (input.nonWitnessUtxo) {
|
|
158
|
-
addNonWitnessTxCache(this
|
|
233
|
+
addNonWitnessTxCache(this.#cache, input, inputIndex);
|
|
159
234
|
}
|
|
160
|
-
c.
|
|
161
|
-
c.
|
|
162
|
-
c.
|
|
235
|
+
c.fee = undefined;
|
|
236
|
+
c.feeRate = undefined;
|
|
237
|
+
c.extractedTx = undefined;
|
|
238
|
+
c.prevOuts = undefined;
|
|
239
|
+
c.signingScripts = undefined;
|
|
240
|
+
c.values = undefined;
|
|
241
|
+
c.taprootHashCache = undefined;
|
|
163
242
|
return this;
|
|
164
243
|
}
|
|
165
|
-
addOutputs(outputDatas) {
|
|
166
|
-
outputDatas.forEach((outputData) => this.addOutput(outputData));
|
|
244
|
+
addOutputs(outputDatas, checkPartialSigs = true) {
|
|
245
|
+
outputDatas.forEach((outputData) => this.addOutput(outputData, checkPartialSigs));
|
|
167
246
|
return this;
|
|
168
247
|
}
|
|
169
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Add an output to the PSBT.
|
|
250
|
+
*
|
|
251
|
+
* **PERFORMANCE WARNING:** Passing an `address` string is ~10x slower than passing
|
|
252
|
+
* a `script` directly due to address parsing overhead (bech32 decode, etc.).
|
|
253
|
+
* For high-performance use cases with many outputs, pre-compute the script using
|
|
254
|
+
* `toOutputScript(address, network)` and pass `{ script, value }` instead.
|
|
255
|
+
*
|
|
256
|
+
* @param outputData - Output data with either `address` or `script`, and `value`
|
|
257
|
+
* @param checkPartialSigs - Whether to check for existing signatures (default: true)
|
|
258
|
+
*/
|
|
259
|
+
addOutput(outputData, checkPartialSigs = true) {
|
|
170
260
|
const hasAddress = 'address' in outputData;
|
|
171
261
|
const hasScript = 'script' in outputData;
|
|
172
|
-
if (
|
|
173
|
-
!outputData ||
|
|
262
|
+
if (!outputData ||
|
|
174
263
|
outputData.value === undefined ||
|
|
175
264
|
(!hasAddress && !hasScript)) {
|
|
176
265
|
throw new Error(`Invalid arguments for Psbt.addOutput. ` +
|
|
177
266
|
`Requires single object with at least [script or address] and [value]`);
|
|
178
267
|
}
|
|
179
|
-
|
|
268
|
+
if (checkPartialSigs) {
|
|
269
|
+
checkInputsForPartialSig(this.data.inputs, 'addOutput', this.#cache.hasSignatures);
|
|
270
|
+
}
|
|
180
271
|
if (hasAddress) {
|
|
181
272
|
const { address } = outputData;
|
|
182
|
-
const { network } = this
|
|
273
|
+
const { network } = this.#opts;
|
|
183
274
|
const script = toOutputScript(address, network);
|
|
184
275
|
outputData = Object.assign({}, outputData, { script });
|
|
185
276
|
}
|
|
186
277
|
checkTaprootOutputFields(outputData, outputData, 'addOutput');
|
|
187
|
-
const c = this
|
|
278
|
+
const c = this.#cache;
|
|
188
279
|
this.data.addOutput(outputData);
|
|
189
|
-
c.
|
|
190
|
-
c.
|
|
191
|
-
c.
|
|
280
|
+
c.fee = undefined;
|
|
281
|
+
c.feeRate = undefined;
|
|
282
|
+
c.extractedTx = undefined;
|
|
283
|
+
c.taprootHashCache = undefined;
|
|
192
284
|
return this;
|
|
193
285
|
}
|
|
194
286
|
extractTransaction(disableFeeCheck, disableOutputChecks) {
|
|
@@ -197,51 +289,53 @@ export class Psbt {
|
|
|
197
289
|
}
|
|
198
290
|
if (!this.data.inputs.every(isFinalized))
|
|
199
291
|
throw new Error('Not finalized');
|
|
200
|
-
const c = this
|
|
292
|
+
const c = this.#cache;
|
|
201
293
|
if (!disableFeeCheck) {
|
|
202
|
-
checkFees(this, c, this
|
|
294
|
+
checkFees(this, c, this.#opts);
|
|
203
295
|
}
|
|
204
|
-
if (c.
|
|
205
|
-
return c.
|
|
206
|
-
const tx = c.
|
|
296
|
+
if (c.extractedTx)
|
|
297
|
+
return c.extractedTx;
|
|
298
|
+
const tx = c.tx.clone();
|
|
207
299
|
inputFinalizeGetAmts(this.data.inputs, tx, c, true, disableOutputChecks);
|
|
208
300
|
return tx;
|
|
209
301
|
}
|
|
210
302
|
getFeeRate(disableOutputChecks = false) {
|
|
211
|
-
return getTxCacheValue('
|
|
303
|
+
return getTxCacheValue('feeRate', 'fee rate', this.data.inputs, this.#cache, disableOutputChecks);
|
|
212
304
|
}
|
|
213
305
|
getFee(disableOutputChecks = false) {
|
|
214
|
-
return getTxCacheValue('
|
|
306
|
+
return getTxCacheValue('fee', 'fee', this.data.inputs, this.#cache, disableOutputChecks);
|
|
215
307
|
}
|
|
216
308
|
finalizeAllInputs() {
|
|
217
|
-
checkForInput(this.data.inputs, 0);
|
|
309
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
218
310
|
range(this.data.inputs.length).forEach((idx) => this.finalizeInput(idx));
|
|
219
311
|
return this;
|
|
220
312
|
}
|
|
221
313
|
finalizeInput(inputIndex, finalScriptsFunc, canRunChecks) {
|
|
222
314
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
223
315
|
if (isTaprootInput(input)) {
|
|
224
|
-
return this
|
|
316
|
+
return this.#finalizeTaprootInput(inputIndex, input, undefined, finalScriptsFunc);
|
|
225
317
|
}
|
|
226
|
-
return this
|
|
318
|
+
return this.#finalizeInput(inputIndex, input, finalScriptsFunc, canRunChecks ?? true);
|
|
227
319
|
}
|
|
228
320
|
finalizeTaprootInput(inputIndex, tapLeafHashToFinalize, finalScriptsFunc = tapScriptFinalizer) {
|
|
229
321
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
230
322
|
if (isTaprootInput(input))
|
|
231
|
-
return this
|
|
323
|
+
return this.#finalizeTaprootInput(inputIndex, input, tapLeafHashToFinalize, finalScriptsFunc);
|
|
232
324
|
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
|
|
233
325
|
}
|
|
234
326
|
getInputType(inputIndex) {
|
|
235
327
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
236
|
-
const script = getScriptFromUtxo(inputIndex, input, this
|
|
237
|
-
const result = getMeaningfulScript(script, inputIndex, 'input', input.redeemScript ||
|
|
328
|
+
const script = getScriptFromUtxo(inputIndex, input, this.#cache);
|
|
329
|
+
const result = getMeaningfulScript(script, inputIndex, 'input', input.redeemScript ||
|
|
330
|
+
redeemFromFinalScriptSig(input.finalScriptSig), input.witnessScript ||
|
|
331
|
+
redeemFromFinalWitnessScript(input.finalScriptWitness));
|
|
238
332
|
const type = result.type === 'raw' ? '' : result.type + '-';
|
|
239
333
|
const mainType = classifyScript(result.meaningfulScript);
|
|
240
334
|
return (type + mainType);
|
|
241
335
|
}
|
|
242
336
|
inputHasPubkey(inputIndex, pubkey) {
|
|
243
337
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
244
|
-
return pubkeyInInput(pubkey, input, inputIndex, this
|
|
338
|
+
return pubkeyInInput(pubkey, input, inputIndex, this.#cache);
|
|
245
339
|
}
|
|
246
340
|
inputHasHDKey(inputIndex, root) {
|
|
247
341
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
@@ -250,7 +344,7 @@ export class Psbt {
|
|
|
250
344
|
}
|
|
251
345
|
outputHasPubkey(outputIndex, pubkey) {
|
|
252
346
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
253
|
-
return pubkeyInOutput(pubkey, output, outputIndex, this
|
|
347
|
+
return pubkeyInOutput(pubkey, output, outputIndex, this.#cache);
|
|
254
348
|
}
|
|
255
349
|
outputHasHDKey(outputIndex, root) {
|
|
256
350
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
@@ -258,15 +352,15 @@ export class Psbt {
|
|
|
258
352
|
return !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine);
|
|
259
353
|
}
|
|
260
354
|
validateSignaturesOfAllInputs(validator) {
|
|
261
|
-
checkForInput(this.data.inputs, 0);
|
|
355
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
262
356
|
const results = range(this.data.inputs.length).map((idx) => this.validateSignaturesOfInput(idx, validator));
|
|
263
357
|
return results.reduce((final, res) => res && final, true);
|
|
264
358
|
}
|
|
265
359
|
validateSignaturesOfInput(inputIndex, validator, pubkey) {
|
|
266
360
|
const input = this.data.inputs[inputIndex];
|
|
267
361
|
if (isTaprootInput(input))
|
|
268
|
-
return this
|
|
269
|
-
return this
|
|
362
|
+
return this.#validateSignaturesOfTaprootInput(inputIndex, validator, pubkey);
|
|
363
|
+
return this.#validateSignaturesOfInput(inputIndex, validator, pubkey);
|
|
270
364
|
}
|
|
271
365
|
signAllInputsHD(hdKeyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
272
366
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
@@ -334,6 +428,9 @@ export class Psbt {
|
|
|
334
428
|
signAllInputs(keyPair, sighashTypes) {
|
|
335
429
|
if (!keyPair || !keyPair.publicKey)
|
|
336
430
|
throw new Error('Need Signer to sign input');
|
|
431
|
+
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
432
|
+
// as input information is added, then eventually
|
|
433
|
+
// optimize this method.
|
|
337
434
|
const results = [];
|
|
338
435
|
for (const i of range(this.data.inputs.length)) {
|
|
339
436
|
try {
|
|
@@ -353,6 +450,9 @@ export class Psbt {
|
|
|
353
450
|
return new Promise((resolve, reject) => {
|
|
354
451
|
if (!keyPair || !keyPair.publicKey)
|
|
355
452
|
return reject(new Error('Need Signer to sign input'));
|
|
453
|
+
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
454
|
+
// as input information is added, then eventually
|
|
455
|
+
// optimize this method.
|
|
356
456
|
const results = [];
|
|
357
457
|
const promises = [];
|
|
358
458
|
for (const [i] of this.data.inputs.entries()) {
|
|
@@ -376,9 +476,9 @@ export class Psbt {
|
|
|
376
476
|
}
|
|
377
477
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
378
478
|
if (isTaprootInput(input)) {
|
|
379
|
-
return this
|
|
479
|
+
return this.#signTaprootInput(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
380
480
|
}
|
|
381
|
-
return this
|
|
481
|
+
return this.#signInput(inputIndex, keyPair, sighashTypes);
|
|
382
482
|
}
|
|
383
483
|
signTaprootInput(inputIndex, keyPair, tapLeafHashToSign, sighashTypes) {
|
|
384
484
|
if (!keyPair || !keyPair.publicKey) {
|
|
@@ -386,7 +486,7 @@ export class Psbt {
|
|
|
386
486
|
}
|
|
387
487
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
388
488
|
if (isTaprootInput(input)) {
|
|
389
|
-
return this
|
|
489
|
+
return this.#signTaprootInput(inputIndex, input, keyPair, tapLeafHashToSign, sighashTypes);
|
|
390
490
|
}
|
|
391
491
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
392
492
|
}
|
|
@@ -396,8 +496,8 @@ export class Psbt {
|
|
|
396
496
|
throw new Error('Need Signer to sign input');
|
|
397
497
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
398
498
|
if (isTaprootInput(input))
|
|
399
|
-
return this
|
|
400
|
-
return this
|
|
499
|
+
return this.#signTaprootInputAsync(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
500
|
+
return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
|
|
401
501
|
});
|
|
402
502
|
}
|
|
403
503
|
signTaprootInputAsync(inputIndex, keyPair, tapLeafHash, sighashTypes) {
|
|
@@ -406,20 +506,20 @@ export class Psbt {
|
|
|
406
506
|
throw new Error('Need Signer to sign input');
|
|
407
507
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
408
508
|
if (isTaprootInput(input))
|
|
409
|
-
return this
|
|
509
|
+
return this.#signTaprootInputAsync(inputIndex, input, keyPair, tapLeafHash, sighashTypes);
|
|
410
510
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
411
511
|
});
|
|
412
512
|
}
|
|
413
513
|
toBuffer() {
|
|
414
|
-
checkCache(this
|
|
415
|
-
return this.data.toBuffer();
|
|
514
|
+
checkCache(this.#cache);
|
|
515
|
+
return new Uint8Array(this.data.toBuffer());
|
|
416
516
|
}
|
|
417
517
|
toHex() {
|
|
418
|
-
checkCache(this
|
|
518
|
+
checkCache(this.#cache);
|
|
419
519
|
return this.data.toHex();
|
|
420
520
|
}
|
|
421
521
|
toBase64() {
|
|
422
|
-
checkCache(this
|
|
522
|
+
checkCache(this.#cache);
|
|
423
523
|
return this.data.toBase64();
|
|
424
524
|
}
|
|
425
525
|
updateGlobal(updateData) {
|
|
@@ -430,9 +530,21 @@ export class Psbt {
|
|
|
430
530
|
if (updateData.witnessScript)
|
|
431
531
|
checkInvalidP2WSH(updateData.witnessScript);
|
|
432
532
|
checkTaprootInputFields(this.data.inputs[inputIndex], updateData, 'updateInput');
|
|
433
|
-
|
|
533
|
+
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
534
|
+
const normalizedUpdate = updateData.witnessUtxo
|
|
535
|
+
? {
|
|
536
|
+
...updateData,
|
|
537
|
+
witnessUtxo: {
|
|
538
|
+
script: updateData.witnessUtxo.script,
|
|
539
|
+
value: typeof updateData.witnessUtxo.value === 'bigint'
|
|
540
|
+
? updateData.witnessUtxo.value
|
|
541
|
+
: BigInt(updateData.witnessUtxo.value),
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
: updateData;
|
|
545
|
+
this.data.updateInput(inputIndex, normalizedUpdate);
|
|
434
546
|
if (updateData.nonWitnessUtxo) {
|
|
435
|
-
addNonWitnessTxCache(this
|
|
547
|
+
addNonWitnessTxCache(this.#cache, this.data.inputs[inputIndex], inputIndex);
|
|
436
548
|
}
|
|
437
549
|
return this;
|
|
438
550
|
}
|
|
@@ -459,18 +571,18 @@ export class Psbt {
|
|
|
459
571
|
return this;
|
|
460
572
|
}
|
|
461
573
|
checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes) {
|
|
462
|
-
if (typeof keyPair.signSchnorr !== 'function')
|
|
574
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
463
575
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
464
|
-
const pubkey =
|
|
576
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
465
577
|
? keyPair.publicKey
|
|
466
|
-
:
|
|
467
|
-
const hashesForSig = getTaprootHashesForSig(inputIndex, input, this.data.inputs, pubkey, this
|
|
578
|
+
: new Uint8Array(keyPair.publicKey);
|
|
579
|
+
const hashesForSig = getTaprootHashesForSig(inputIndex, input, this.data.inputs, pubkey, this.#cache, tapLeafHashToSign, allowedSighashTypes);
|
|
468
580
|
if (!hashesForSig || !hashesForSig.length)
|
|
469
|
-
throw new Error(`Can not sign for input #${inputIndex} with the key ${pubkey
|
|
581
|
+
throw new Error(`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`);
|
|
470
582
|
return hashesForSig;
|
|
471
583
|
}
|
|
472
|
-
|
|
473
|
-
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(inputIndex, input, this
|
|
584
|
+
#finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts, canRunChecks = true) {
|
|
585
|
+
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(inputIndex, input, this.#cache);
|
|
474
586
|
if (!script)
|
|
475
587
|
throw new Error(`No script found for input #${inputIndex}`);
|
|
476
588
|
checkPartialSigSighashes(input);
|
|
@@ -484,9 +596,10 @@ export class Psbt {
|
|
|
484
596
|
this.data.clearFinalizedInput(inputIndex);
|
|
485
597
|
return this;
|
|
486
598
|
}
|
|
487
|
-
|
|
599
|
+
#finalizeTaprootInput(inputIndex, input, tapLeafHashToFinalize, finalScriptsFunc = tapScriptFinalizer) {
|
|
488
600
|
if (!input.witnessUtxo)
|
|
489
601
|
throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
|
|
602
|
+
// Check key spend first. Increased privacy and reduced block space.
|
|
490
603
|
if (input.tapKeySig) {
|
|
491
604
|
const payment = payments.p2tr({
|
|
492
605
|
output: input.witnessUtxo.script,
|
|
@@ -504,14 +617,16 @@ export class Psbt {
|
|
|
504
617
|
this.data.clearFinalizedInput(inputIndex);
|
|
505
618
|
return this;
|
|
506
619
|
}
|
|
507
|
-
|
|
620
|
+
#validateSignaturesOfInput(inputIndex, validator, pubkey) {
|
|
508
621
|
const input = this.data.inputs[inputIndex];
|
|
509
622
|
const partialSig = (input || {}).partialSig;
|
|
510
623
|
if (!input || !partialSig || partialSig.length < 1)
|
|
511
624
|
throw new Error('No signatures to validate');
|
|
512
625
|
if (typeof validator !== 'function')
|
|
513
626
|
throw new Error('Need validator function to validate signatures');
|
|
514
|
-
const mySigs = pubkey
|
|
627
|
+
const mySigs = pubkey
|
|
628
|
+
? partialSig.filter((sig) => equals(sig.pubkey, pubkey))
|
|
629
|
+
: partialSig;
|
|
515
630
|
if (mySigs.length < 1)
|
|
516
631
|
throw new Error('No signatures for this pubkey');
|
|
517
632
|
const results = [];
|
|
@@ -519,21 +634,23 @@ export class Psbt {
|
|
|
519
634
|
let scriptCache;
|
|
520
635
|
let sighashCache;
|
|
521
636
|
for (const pSig of mySigs) {
|
|
522
|
-
const
|
|
637
|
+
const pSigSignature = pSig.signature;
|
|
638
|
+
const pSigPubkey = pSig.pubkey;
|
|
639
|
+
const sig = bscript.signature.decode(pSigSignature);
|
|
523
640
|
const { hash, script } = sighashCache !== sig.hashType || !hashCache || !scriptCache
|
|
524
641
|
? getHashForSig(inputIndex, Object.assign({}, input, {
|
|
525
642
|
sighashType: sig.hashType,
|
|
526
|
-
}), this
|
|
643
|
+
}), this.#cache, true)
|
|
527
644
|
: { hash: hashCache, script: scriptCache };
|
|
528
645
|
sighashCache = sig.hashType;
|
|
529
646
|
hashCache = hash;
|
|
530
647
|
scriptCache = script;
|
|
531
|
-
checkScriptForPubkey(
|
|
532
|
-
results.push(validator(
|
|
648
|
+
checkScriptForPubkey(pSigPubkey, script, 'verify');
|
|
649
|
+
results.push(validator(pSigPubkey, hash, sig.signature));
|
|
533
650
|
}
|
|
534
651
|
return results.every((res) => res);
|
|
535
652
|
}
|
|
536
|
-
validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
|
|
653
|
+
#validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
|
|
537
654
|
const input = this.data.inputs[inputIndex];
|
|
538
655
|
const tapKeySig = (input || {}).tapKeySig;
|
|
539
656
|
const tapScriptSig = (input || {}).tapScriptSig;
|
|
@@ -541,10 +658,10 @@ export class Psbt {
|
|
|
541
658
|
throw new Error('No signatures to validate');
|
|
542
659
|
if (typeof validator !== 'function')
|
|
543
660
|
throw new Error('Need validator function to validate signatures');
|
|
544
|
-
|
|
545
|
-
const allHashses =
|
|
546
|
-
? getTaprootHashesForSig(inputIndex, input, this.data.inputs,
|
|
547
|
-
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this
|
|
661
|
+
const xPubkey = pubkey ? toXOnly(pubkey) : undefined;
|
|
662
|
+
const allHashses = xPubkey
|
|
663
|
+
? getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey, this.#cache)
|
|
664
|
+
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this.#cache);
|
|
548
665
|
if (!allHashses.length)
|
|
549
666
|
throw new Error('No signatures for this pubkey');
|
|
550
667
|
const tapKeyHash = allHashses.find((h) => !h.leafHash);
|
|
@@ -557,9 +674,10 @@ export class Psbt {
|
|
|
557
674
|
}
|
|
558
675
|
if (tapScriptSig) {
|
|
559
676
|
for (const tapSig of tapScriptSig) {
|
|
560
|
-
const
|
|
677
|
+
const tapSigPubkey = tapSig.pubkey;
|
|
678
|
+
const tapSigHash = allHashses.find((h) => equals(tapSigPubkey, h.pubkey));
|
|
561
679
|
if (tapSigHash) {
|
|
562
|
-
const isValidTapScriptSig = validator(
|
|
680
|
+
const isValidTapScriptSig = validator(tapSigPubkey, tapSigHash.hash, trimTaprootSig(tapSig.signature));
|
|
563
681
|
if (!isValidTapScriptSig)
|
|
564
682
|
return false;
|
|
565
683
|
validationResultCount++;
|
|
@@ -568,53 +686,58 @@ export class Psbt {
|
|
|
568
686
|
}
|
|
569
687
|
return validationResultCount > 0;
|
|
570
688
|
}
|
|
571
|
-
|
|
572
|
-
const pubkey =
|
|
689
|
+
#signInput(inputIndex, keyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
690
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
573
691
|
? keyPair.publicKey
|
|
574
|
-
:
|
|
575
|
-
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this
|
|
692
|
+
: new Uint8Array(keyPair.publicKey);
|
|
693
|
+
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this.#cache, sighashTypes);
|
|
576
694
|
const sig = keyPair.sign(hash);
|
|
577
695
|
const partialSig = [
|
|
578
696
|
{
|
|
579
697
|
pubkey,
|
|
580
|
-
signature: bscript.signature.encode(
|
|
698
|
+
signature: bscript.signature.encode(sig instanceof Uint8Array ? sig : new Uint8Array(sig), sighashType),
|
|
581
699
|
},
|
|
582
700
|
];
|
|
583
701
|
this.data.updateInput(inputIndex, { partialSig });
|
|
702
|
+
this.#cache.hasSignatures = true;
|
|
584
703
|
return this;
|
|
585
704
|
}
|
|
586
|
-
|
|
587
|
-
const pubkey =
|
|
705
|
+
#signTaprootInput(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes = [Transaction.SIGHASH_DEFAULT]) {
|
|
706
|
+
const pubkey = (keyPair.publicKey instanceof Uint8Array
|
|
588
707
|
? keyPair.publicKey
|
|
589
|
-
:
|
|
708
|
+
: new Uint8Array(keyPair.publicKey));
|
|
709
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
710
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
711
|
+
// checkTaprootHashesForSig validates signSchnorr exists
|
|
590
712
|
const hashesForSig = this.checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes);
|
|
591
713
|
const signSchnorr = keyPair.signSchnorr.bind(keyPair);
|
|
592
|
-
const toBuffer = (data) => Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
593
714
|
const tapKeySig = hashesForSig
|
|
594
715
|
.filter((h) => !h.leafHash)
|
|
595
|
-
.map((h) => serializeTaprootSignature(
|
|
716
|
+
.map((h) => serializeTaprootSignature(signSchnorr(h.hash), input.sighashType))[0];
|
|
596
717
|
const tapScriptSig = hashesForSig
|
|
597
718
|
.filter((h) => !!h.leafHash)
|
|
598
719
|
.map((h) => ({
|
|
599
720
|
pubkey: toXOnly(pubkey),
|
|
600
|
-
signature: serializeTaprootSignature(
|
|
721
|
+
signature: serializeTaprootSignature(signSchnorr(h.hash), input.sighashType),
|
|
601
722
|
leafHash: h.leafHash,
|
|
602
723
|
}));
|
|
603
724
|
if (tapKeySig) {
|
|
604
725
|
this.data.updateInput(inputIndex, { tapKeySig });
|
|
726
|
+
this.#cache.hasSignatures = true;
|
|
605
727
|
}
|
|
606
728
|
if (tapScriptSig.length) {
|
|
607
729
|
this.data.updateInput(inputIndex, { tapScriptSig });
|
|
730
|
+
this.#cache.hasSignatures = true;
|
|
608
731
|
}
|
|
609
732
|
return this;
|
|
610
733
|
}
|
|
611
|
-
|
|
612
|
-
const pubkey =
|
|
734
|
+
#signInputAsync(inputIndex, keyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
735
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
613
736
|
? keyPair.publicKey
|
|
614
|
-
:
|
|
615
|
-
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this
|
|
737
|
+
: new Uint8Array(keyPair.publicKey);
|
|
738
|
+
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this.#cache, sighashTypes);
|
|
616
739
|
return Promise.resolve(keyPair.sign(hash)).then((signature) => {
|
|
617
|
-
const sig =
|
|
740
|
+
const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
|
|
618
741
|
const partialSig = [
|
|
619
742
|
{
|
|
620
743
|
pubkey,
|
|
@@ -622,21 +745,24 @@ export class Psbt {
|
|
|
622
745
|
},
|
|
623
746
|
];
|
|
624
747
|
this.data.updateInput(inputIndex, { partialSig });
|
|
748
|
+
this.#cache.hasSignatures = true;
|
|
625
749
|
});
|
|
626
750
|
}
|
|
627
|
-
async
|
|
628
|
-
const pubkey =
|
|
751
|
+
async #signTaprootInputAsync(inputIndex, input, keyPair, tapLeafHash, sighashTypes = [Transaction.SIGHASH_DEFAULT]) {
|
|
752
|
+
const pubkey = (keyPair.publicKey instanceof Uint8Array
|
|
629
753
|
? keyPair.publicKey
|
|
630
|
-
:
|
|
754
|
+
: new Uint8Array(keyPair.publicKey));
|
|
755
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
756
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
757
|
+
// checkTaprootHashesForSig validates signSchnorr exists
|
|
631
758
|
const hashesForSig = this.checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHash, sighashTypes);
|
|
632
759
|
const signSchnorr = keyPair.signSchnorr.bind(keyPair);
|
|
633
|
-
const toBuffer = (data) => Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
634
760
|
const signaturePromises = [];
|
|
635
761
|
const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
|
|
636
762
|
if (tapKeyHash) {
|
|
637
763
|
const tapKeySigPromise = Promise.resolve(signSchnorr(tapKeyHash.hash)).then((sig) => {
|
|
638
764
|
return {
|
|
639
|
-
tapKeySig: serializeTaprootSignature(
|
|
765
|
+
tapKeySig: serializeTaprootSignature(sig, input.sighashType),
|
|
640
766
|
};
|
|
641
767
|
});
|
|
642
768
|
signaturePromises.push(tapKeySigPromise);
|
|
@@ -648,7 +774,7 @@ export class Psbt {
|
|
|
648
774
|
const tapScriptSig = [
|
|
649
775
|
{
|
|
650
776
|
pubkey: toXOnly(pubkey),
|
|
651
|
-
signature: serializeTaprootSignature(
|
|
777
|
+
signature: serializeTaprootSignature(signature, input.sighashType),
|
|
652
778
|
leafHash: tsh.leafHash,
|
|
653
779
|
},
|
|
654
780
|
];
|
|
@@ -659,12 +785,23 @@ export class Psbt {
|
|
|
659
785
|
const results = await Promise.all(signaturePromises);
|
|
660
786
|
for (const v of results) {
|
|
661
787
|
this.data.updateInput(inputIndex, v);
|
|
788
|
+
this.#cache.hasSignatures = true;
|
|
662
789
|
}
|
|
663
790
|
}
|
|
664
791
|
}
|
|
792
|
+
/**
|
|
793
|
+
* This function is needed to pass to the bip174 base class's fromBuffer.
|
|
794
|
+
* It takes the "transaction buffer" portion of the psbt buffer and returns a
|
|
795
|
+
* Transaction (From the bip174 library) interface.
|
|
796
|
+
*/
|
|
665
797
|
const transactionFromBuffer = (buffer) => new PsbtTransaction(buffer);
|
|
798
|
+
/**
|
|
799
|
+
* This class implements the Transaction interface from bip174 library.
|
|
800
|
+
* It contains a bitcoinjs-lib Transaction object.
|
|
801
|
+
*/
|
|
666
802
|
class PsbtTransaction {
|
|
667
|
-
|
|
803
|
+
tx;
|
|
804
|
+
constructor(buffer = new Uint8Array([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
|
668
805
|
this.tx = Transaction.fromBuffer(buffer);
|
|
669
806
|
checkTxEmpty(this.tx);
|
|
670
807
|
Object.defineProperty(this, 'tx', {
|
|
@@ -681,20 +818,18 @@ class PsbtTransaction {
|
|
|
681
818
|
addInput(input) {
|
|
682
819
|
if (input.hash === undefined ||
|
|
683
820
|
input.index === undefined ||
|
|
684
|
-
(!
|
|
821
|
+
(!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
|
|
685
822
|
typeof input.index !== 'number') {
|
|
686
823
|
throw new Error('Error adding input.');
|
|
687
824
|
}
|
|
688
|
-
const hash = typeof input.hash === 'string'
|
|
689
|
-
? reverseBuffer(Buffer.from(input.hash, 'hex'))
|
|
690
|
-
: input.hash;
|
|
825
|
+
const hash = (typeof input.hash === 'string' ? reverse(fromHex(input.hash)) : input.hash);
|
|
691
826
|
this.tx.addInput(hash, input.index, input.sequence);
|
|
692
827
|
}
|
|
693
828
|
addOutput(output) {
|
|
694
829
|
if (output.script === undefined ||
|
|
695
830
|
output.value === undefined ||
|
|
696
|
-
!
|
|
697
|
-
typeof output.value !== '
|
|
831
|
+
!(output.script instanceof Uint8Array) ||
|
|
832
|
+
typeof output.value !== 'bigint') {
|
|
698
833
|
throw new Error('Error adding output.');
|
|
699
834
|
}
|
|
700
835
|
this.tx.addOutput(output.script, output.value);
|
|
@@ -713,6 +848,8 @@ function canFinalize(input, script, scriptType) {
|
|
|
713
848
|
const p2ms = payments.p2ms({
|
|
714
849
|
output: script,
|
|
715
850
|
});
|
|
851
|
+
if (p2ms.m === undefined)
|
|
852
|
+
throw new Error('Cannot determine m for multisig');
|
|
716
853
|
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
|
|
717
854
|
}
|
|
718
855
|
case 'nonstandard':
|
|
@@ -721,11 +858,6 @@ function canFinalize(input, script, scriptType) {
|
|
|
721
858
|
return false;
|
|
722
859
|
}
|
|
723
860
|
}
|
|
724
|
-
function checkCache(cache) {
|
|
725
|
-
if (cache.__UNSAFE_SIGN_NONSEGWIT) {
|
|
726
|
-
throw new Error('Not BIP174 compliant, can not export');
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
861
|
function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
730
862
|
if (!partialSig)
|
|
731
863
|
return false;
|
|
@@ -734,7 +866,7 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
|
734
866
|
sigs = pubkeys
|
|
735
867
|
.map((pkey) => {
|
|
736
868
|
const pubkey = compressPubkey(pkey);
|
|
737
|
-
return partialSig.find((pSig) => pSig.pubkey
|
|
869
|
+
return partialSig.find((pSig) => equals(pSig.pubkey, pubkey));
|
|
738
870
|
})
|
|
739
871
|
.filter((v) => !!v);
|
|
740
872
|
}
|
|
@@ -745,31 +877,25 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
|
745
877
|
throw new Error('Too many signatures');
|
|
746
878
|
return sigs.length === neededSigs;
|
|
747
879
|
}
|
|
748
|
-
function isFinalized(input) {
|
|
749
|
-
return !!input.finalScriptSig || !!input.finalScriptWitness;
|
|
750
|
-
}
|
|
751
880
|
function bip32DerivationIsMine(root) {
|
|
752
881
|
return (d) => {
|
|
753
|
-
const fingerprint =
|
|
882
|
+
const fingerprint = root.fingerprint instanceof Uint8Array
|
|
754
883
|
? root.fingerprint
|
|
755
|
-
:
|
|
756
|
-
if (!d.masterFingerprint
|
|
884
|
+
: new Uint8Array(root.fingerprint);
|
|
885
|
+
if (!equals(d.masterFingerprint, fingerprint))
|
|
757
886
|
return false;
|
|
758
887
|
const derivedPubkey = root.derivePath(d.path).publicKey;
|
|
759
|
-
const pubkey =
|
|
760
|
-
if (!
|
|
888
|
+
const pubkey = derivedPubkey instanceof Uint8Array ? derivedPubkey : new Uint8Array(derivedPubkey);
|
|
889
|
+
if (!equals(pubkey, d.pubkey))
|
|
761
890
|
return false;
|
|
762
891
|
return true;
|
|
763
892
|
};
|
|
764
893
|
}
|
|
765
|
-
function check32Bit(num) {
|
|
766
|
-
if (typeof num !== 'number' || num !== Math.floor(num) || num > 0xffffffff || num < 0) {
|
|
767
|
-
throw new Error('Invalid 32 bit integer');
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
894
|
function checkFees(psbt, cache, opts) {
|
|
771
|
-
const feeRate = cache.
|
|
772
|
-
|
|
895
|
+
const feeRate = cache.feeRate || psbt.getFeeRate();
|
|
896
|
+
if (!cache.extractedTx)
|
|
897
|
+
throw new Error('Transaction not extracted');
|
|
898
|
+
const vsize = cache.extractedTx.virtualSize();
|
|
773
899
|
const satoshis = feeRate * vsize;
|
|
774
900
|
if (feeRate >= opts.maximumFeeRate) {
|
|
775
901
|
throw new Error(`Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
|
|
@@ -779,81 +905,24 @@ function checkFees(psbt, cache, opts) {
|
|
|
779
905
|
`pass true to the first arg of extractTransaction.`);
|
|
780
906
|
}
|
|
781
907
|
}
|
|
782
|
-
function checkInputsForPartialSig(inputs, action) {
|
|
783
|
-
inputs.forEach((input) => {
|
|
784
|
-
const throws = isTaprootInput(input)
|
|
785
|
-
? checkTaprootInputForSigs(input, action)
|
|
786
|
-
: checkInputForSig(input, action);
|
|
787
|
-
if (throws)
|
|
788
|
-
throw new Error('Can not modify transaction, signatures exist.');
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
function checkPartialSigSighashes(input) {
|
|
792
|
-
if (!input.sighashType || !input.partialSig)
|
|
793
|
-
return;
|
|
794
|
-
const { partialSig, sighashType } = input;
|
|
795
|
-
partialSig.forEach((pSig) => {
|
|
796
|
-
const { hashType } = bscript.signature.decode(pSig.signature);
|
|
797
|
-
if (sighashType !== hashType) {
|
|
798
|
-
throw new Error('Signature sighash does not match input sighash type');
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
function checkScriptForPubkey(pubkey, script, action) {
|
|
803
|
-
if (!pubkeyInScript(pubkey, script)) {
|
|
804
|
-
throw new Error(`Can not ${action} for this input with the key ${pubkey.toString('hex')}`);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
function checkTxEmpty(tx) {
|
|
808
|
-
const isEmpty = tx.ins.every((input) => input.script &&
|
|
809
|
-
input.script.length === 0 &&
|
|
810
|
-
input.witness &&
|
|
811
|
-
input.witness.length === 0);
|
|
812
|
-
if (!isEmpty) {
|
|
813
|
-
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
function checkTxForDupeIns(tx, cache) {
|
|
817
|
-
tx.ins.forEach((input) => {
|
|
818
|
-
checkTxInputCache(cache, input);
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
function checkTxInputCache(cache, input) {
|
|
822
|
-
const key = `${reverseBuffer(Buffer.from(input.hash)).toString('hex')}:${input.index}`;
|
|
823
|
-
if (cache.__TX_IN_CACHE[key])
|
|
824
|
-
throw new Error('Duplicate input detected.');
|
|
825
|
-
cache.__TX_IN_CACHE[key] = 1;
|
|
826
|
-
}
|
|
827
|
-
function scriptCheckerFactory(payment, paymentScriptName) {
|
|
828
|
-
return (inputIndex, scriptPubKey, redeemScript, ioType) => {
|
|
829
|
-
const redeemScriptOutput = payment({
|
|
830
|
-
redeem: { output: redeemScript },
|
|
831
|
-
}).output;
|
|
832
|
-
if (!scriptPubKey.equals(redeemScriptOutput)) {
|
|
833
|
-
throw new Error(`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`);
|
|
834
|
-
}
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
const checkRedeemScript = scriptCheckerFactory(payments.p2sh, 'Redeem script');
|
|
838
|
-
const checkWitnessScript = scriptCheckerFactory(payments.p2wsh, 'Witness script');
|
|
839
908
|
function getTxCacheValue(key, name, inputs, c, disableOutputChecks = false) {
|
|
840
909
|
if (!inputs.every(isFinalized))
|
|
841
910
|
throw new Error(`PSBT must be finalized to calculate ${name}`);
|
|
842
|
-
if (key === '
|
|
843
|
-
return c.
|
|
844
|
-
if (key === '
|
|
845
|
-
return c.
|
|
911
|
+
if (key === 'feeRate' && c.feeRate)
|
|
912
|
+
return c.feeRate;
|
|
913
|
+
if (key === 'fee' && c.fee)
|
|
914
|
+
return c.fee;
|
|
846
915
|
let tx;
|
|
847
916
|
let mustFinalize = true;
|
|
848
|
-
if (c.
|
|
849
|
-
tx = c.
|
|
917
|
+
if (c.extractedTx) {
|
|
918
|
+
tx = c.extractedTx;
|
|
850
919
|
mustFinalize = false;
|
|
851
920
|
}
|
|
852
921
|
else {
|
|
853
|
-
tx = c.
|
|
922
|
+
tx = c.tx.clone();
|
|
854
923
|
}
|
|
855
924
|
inputFinalizeGetAmts(inputs, tx, c, mustFinalize, disableOutputChecks);
|
|
856
|
-
const value = key === '
|
|
925
|
+
const value = key === 'feeRate' ? c.feeRate : c.fee;
|
|
857
926
|
if (value === undefined)
|
|
858
927
|
throw new Error(`Failed to calculate ${name}`);
|
|
859
928
|
return value;
|
|
@@ -863,23 +932,27 @@ export function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP
|
|
|
863
932
|
if (!canFinalize(input, script, scriptType) && canRunChecks) {
|
|
864
933
|
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
865
934
|
}
|
|
935
|
+
if (!input.partialSig)
|
|
936
|
+
throw new Error('Input missing partial signatures');
|
|
866
937
|
return prepareFinalScripts(script, scriptType, input.partialSig, isSegwit, isP2SH, isP2WSH, solution);
|
|
867
938
|
}
|
|
868
939
|
export function prepareFinalScripts(script, scriptType, partialSig, isSegwit, isP2SH, isP2WSH, solution) {
|
|
869
940
|
let finalScriptSig;
|
|
870
941
|
let finalScriptWitness;
|
|
942
|
+
// Wow, the payments API is very handy
|
|
871
943
|
const payment = getPayment(script, scriptType, partialSig);
|
|
872
944
|
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
|
|
873
945
|
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
|
|
874
946
|
if (isSegwit) {
|
|
875
|
-
if (p2wsh) {
|
|
947
|
+
if (p2wsh && p2wsh.witness) {
|
|
876
948
|
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
|
|
877
949
|
}
|
|
878
|
-
else if (payment) {
|
|
950
|
+
else if (payment && payment.witness) {
|
|
879
951
|
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
880
952
|
}
|
|
881
953
|
else {
|
|
882
|
-
|
|
954
|
+
// nonstandard segwit script
|
|
955
|
+
finalScriptWitness = witnessStackToScriptWitness(solution ?? [new Uint8Array([0x00])]);
|
|
883
956
|
}
|
|
884
957
|
if (p2sh) {
|
|
885
958
|
finalScriptSig = p2sh?.input;
|
|
@@ -891,8 +964,7 @@ export function prepareFinalScripts(script, scriptType, partialSig, isSegwit, is
|
|
|
891
964
|
}
|
|
892
965
|
else {
|
|
893
966
|
if (!payment) {
|
|
894
|
-
finalScriptSig =
|
|
895
|
-
Array.isArray(solution) && solution[0] ? solution[0] : Buffer.from([0x01]);
|
|
967
|
+
finalScriptSig = (Array.isArray(solution) && solution[0] ? solution[0] : new Uint8Array([0x01]));
|
|
896
968
|
}
|
|
897
969
|
else {
|
|
898
970
|
finalScriptSig = payment.input;
|
|
@@ -914,7 +986,7 @@ function getHashAndSighashType(inputs, inputIndex, pubkey, cache, sighashTypes)
|
|
|
914
986
|
};
|
|
915
987
|
}
|
|
916
988
|
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
917
|
-
const unsignedTx = cache.
|
|
989
|
+
const unsignedTx = cache.tx;
|
|
918
990
|
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
|
919
991
|
checkSighashTypeAllowed(sighashType, sighashTypes);
|
|
920
992
|
let hash;
|
|
@@ -923,33 +995,42 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
923
995
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
924
996
|
const prevoutHash = unsignedTx.ins[inputIndex].hash;
|
|
925
997
|
const utxoHash = nonWitnessUtxoTx.getHash();
|
|
926
|
-
|
|
998
|
+
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
|
|
999
|
+
if (!equals(prevoutHash, utxoHash)) {
|
|
927
1000
|
throw new Error(`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`);
|
|
928
1001
|
}
|
|
929
1002
|
const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
|
930
1003
|
prevout = nonWitnessUtxoTx.outs[prevoutIndex];
|
|
931
1004
|
}
|
|
932
1005
|
else if (input.witnessUtxo) {
|
|
933
|
-
prevout =
|
|
1006
|
+
prevout = {
|
|
1007
|
+
script: input.witnessUtxo.script,
|
|
1008
|
+
value: input.witnessUtxo.value,
|
|
1009
|
+
};
|
|
934
1010
|
}
|
|
935
1011
|
else {
|
|
936
1012
|
throw new Error('Need a Utxo input item for signing');
|
|
937
1013
|
}
|
|
938
1014
|
const { meaningfulScript, type } = getMeaningfulScript(prevout.script, inputIndex, 'input', input.redeemScript, input.witnessScript);
|
|
1015
|
+
const script = meaningfulScript;
|
|
939
1016
|
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
|
|
940
|
-
hash = unsignedTx.hashForWitnessV0(inputIndex,
|
|
1017
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, script, prevout.value, sighashType);
|
|
941
1018
|
}
|
|
942
1019
|
else if (isP2WPKH(meaningfulScript)) {
|
|
943
|
-
|
|
1020
|
+
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1021
|
+
const p2pkhPayment = payments.p2pkh({
|
|
944
1022
|
hash: meaningfulScript.subarray(2),
|
|
945
|
-
})
|
|
946
|
-
|
|
1023
|
+
});
|
|
1024
|
+
if (!p2pkhPayment.output)
|
|
1025
|
+
throw new Error('Unable to create signing script');
|
|
1026
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, p2pkhPayment.output, prevout.value, sighashType);
|
|
947
1027
|
}
|
|
948
1028
|
else {
|
|
949
|
-
|
|
1029
|
+
// non-segwit
|
|
1030
|
+
if (input.nonWitnessUtxo === undefined && !cache.unsafeSignNonSegwit)
|
|
950
1031
|
throw new Error(`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
|
951
|
-
meaningfulScript
|
|
952
|
-
if (!forValidate && cache.
|
|
1032
|
+
toHex(meaningfulScript));
|
|
1033
|
+
if (!forValidate && cache.unsafeSignNonSegwit)
|
|
953
1034
|
console.warn('Warning: Signing non-segwit inputs without the full parent transaction ' +
|
|
954
1035
|
'means there is a chance that a miner could feed you incorrect information ' +
|
|
955
1036
|
"to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
|
|
@@ -957,10 +1038,10 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
957
1038
|
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
|
|
958
1039
|
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
|
|
959
1040
|
'*********************');
|
|
960
|
-
hash = unsignedTx.hashForSignature(inputIndex,
|
|
1041
|
+
hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
|
|
961
1042
|
}
|
|
962
1043
|
return {
|
|
963
|
-
script
|
|
1044
|
+
script,
|
|
964
1045
|
sighashType,
|
|
965
1046
|
hash,
|
|
966
1047
|
};
|
|
@@ -982,24 +1063,34 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) {
|
|
|
982
1063
|
}
|
|
983
1064
|
function getPrevoutTaprootKey(inputIndex, input, cache) {
|
|
984
1065
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
985
|
-
return isP2TR(script) ?
|
|
1066
|
+
return isP2TR(script) ? script.subarray(2, 34) : null;
|
|
986
1067
|
}
|
|
987
1068
|
function trimTaprootSig(signature) {
|
|
988
|
-
return signature.length === 64 ? signature :
|
|
1069
|
+
return signature.length === 64 ? signature : signature.subarray(0, 64);
|
|
989
1070
|
}
|
|
990
1071
|
function getTaprootHashesForSig(inputIndex, input, inputs, pubkey, cache, tapLeafHashToSign, allowedSighashTypes) {
|
|
991
|
-
const unsignedTx = cache.
|
|
1072
|
+
const unsignedTx = cache.tx;
|
|
992
1073
|
const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
|
|
993
1074
|
checkSighashTypeAllowed(sighashType, allowedSighashTypes);
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1075
|
+
if (!cache.prevOuts) {
|
|
1076
|
+
const prevOuts = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache));
|
|
1077
|
+
cache.prevOuts = prevOuts;
|
|
1078
|
+
cache.signingScripts = prevOuts.map((o) => o.script);
|
|
1079
|
+
cache.values = prevOuts.map((o) => o.value);
|
|
1080
|
+
}
|
|
1081
|
+
const signingScripts = cache.signingScripts;
|
|
1082
|
+
const values = cache.values;
|
|
1083
|
+
// Compute taproot hash cache once for all inputs (O(n) -> O(1) per input)
|
|
1084
|
+
if (!cache.taprootHashCache) {
|
|
1085
|
+
cache.taprootHashCache = unsignedTx.getTaprootHashCache(signingScripts, values);
|
|
1086
|
+
}
|
|
1087
|
+
const taprootCache = cache.taprootHashCache;
|
|
997
1088
|
const hashes = [];
|
|
998
1089
|
if (input.tapInternalKey && !tapLeafHashToSign) {
|
|
999
|
-
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) ||
|
|
1000
|
-
if (toXOnly(pubkey)
|
|
1001
|
-
const tapKeyHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType);
|
|
1002
|
-
hashes.push({ pubkey, hash: tapKeyHash });
|
|
1090
|
+
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || new Uint8Array(0);
|
|
1091
|
+
if (equals(toXOnly(pubkey), outputKey)) {
|
|
1092
|
+
const tapKeyHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, undefined, undefined, taprootCache);
|
|
1093
|
+
hashes.push({ pubkey: pubkey, hash: tapKeyHash });
|
|
1003
1094
|
}
|
|
1004
1095
|
}
|
|
1005
1096
|
const tapLeafHashes = (input.tapLeafScript || [])
|
|
@@ -1011,11 +1102,11 @@ function getTaprootHashesForSig(inputIndex, input, inputs, pubkey, cache, tapLea
|
|
|
1011
1102
|
});
|
|
1012
1103
|
return Object.assign({ hash }, tapLeaf);
|
|
1013
1104
|
})
|
|
1014
|
-
.filter((tapLeaf) => !tapLeafHashToSign ||
|
|
1105
|
+
.filter((tapLeaf) => !tapLeafHashToSign || equals(tapLeafHashToSign, tapLeaf.hash))
|
|
1015
1106
|
.map((tapLeaf) => {
|
|
1016
|
-
const tapScriptHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, tapLeaf.hash);
|
|
1107
|
+
const tapScriptHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, tapLeaf.hash, undefined, taprootCache);
|
|
1017
1108
|
return {
|
|
1018
|
-
pubkey,
|
|
1109
|
+
pubkey: pubkey,
|
|
1019
1110
|
hash: tapScriptHash,
|
|
1020
1111
|
leafHash: tapLeaf.hash,
|
|
1021
1112
|
};
|
|
@@ -1030,41 +1121,38 @@ function checkSighashTypeAllowed(sighashType, sighashTypes) {
|
|
|
1030
1121
|
}
|
|
1031
1122
|
}
|
|
1032
1123
|
function getPayment(script, scriptType, partialSig) {
|
|
1033
|
-
|
|
1124
|
+
const scriptBranded = script;
|
|
1034
1125
|
switch (scriptType) {
|
|
1035
1126
|
case 'multisig': {
|
|
1036
1127
|
const sigs = getSortedSigs(script, partialSig);
|
|
1037
|
-
|
|
1038
|
-
output:
|
|
1128
|
+
return payments.p2ms({
|
|
1129
|
+
output: scriptBranded,
|
|
1039
1130
|
signatures: sigs,
|
|
1040
1131
|
});
|
|
1041
|
-
break;
|
|
1042
1132
|
}
|
|
1043
1133
|
case 'pubkey':
|
|
1044
|
-
|
|
1045
|
-
output:
|
|
1134
|
+
return payments.p2pk({
|
|
1135
|
+
output: scriptBranded,
|
|
1046
1136
|
signature: partialSig[0].signature,
|
|
1047
1137
|
});
|
|
1048
|
-
break;
|
|
1049
1138
|
case 'pubkeyhash':
|
|
1050
|
-
|
|
1051
|
-
output:
|
|
1139
|
+
return payments.p2pkh({
|
|
1140
|
+
output: scriptBranded,
|
|
1052
1141
|
pubkey: partialSig[0].pubkey,
|
|
1053
1142
|
signature: partialSig[0].signature,
|
|
1054
1143
|
});
|
|
1055
|
-
break;
|
|
1056
1144
|
case 'witnesspubkeyhash':
|
|
1057
|
-
|
|
1058
|
-
output:
|
|
1145
|
+
return payments.p2wpkh({
|
|
1146
|
+
output: scriptBranded,
|
|
1059
1147
|
pubkey: partialSig[0].pubkey,
|
|
1060
1148
|
signature: partialSig[0].signature,
|
|
1061
1149
|
});
|
|
1062
|
-
|
|
1150
|
+
default:
|
|
1151
|
+
throw new Error(`Unknown script type: ${scriptType}`);
|
|
1063
1152
|
}
|
|
1064
|
-
return payment;
|
|
1065
1153
|
}
|
|
1066
1154
|
function getScriptFromInput(inputIndex, input, cache) {
|
|
1067
|
-
const unsignedTx = cache.
|
|
1155
|
+
const unsignedTx = cache.tx;
|
|
1068
1156
|
const res = {
|
|
1069
1157
|
script: null,
|
|
1070
1158
|
isSegwit: false,
|
|
@@ -1089,7 +1177,7 @@ function getScriptFromInput(inputIndex, input, cache) {
|
|
|
1089
1177
|
res.script = input.witnessUtxo.script;
|
|
1090
1178
|
}
|
|
1091
1179
|
}
|
|
1092
|
-
if (input.witnessScript || isP2WPKH(res.script)) {
|
|
1180
|
+
if (input.witnessScript || (res.script && isP2WPKH(res.script))) {
|
|
1093
1181
|
res.isSegwit = true;
|
|
1094
1182
|
}
|
|
1095
1183
|
else {
|
|
@@ -1110,7 +1198,7 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1110
1198
|
}
|
|
1111
1199
|
const myDerivations = input.bip32Derivation
|
|
1112
1200
|
.map((bipDv) => {
|
|
1113
|
-
if (bipDv.masterFingerprint
|
|
1201
|
+
if (equals(bipDv.masterFingerprint, hdKeyPair.fingerprint)) {
|
|
1114
1202
|
return bipDv;
|
|
1115
1203
|
}
|
|
1116
1204
|
else {
|
|
@@ -1123,7 +1211,7 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1123
1211
|
}
|
|
1124
1212
|
return myDerivations.map((bipDv) => {
|
|
1125
1213
|
const node = hdKeyPair.derivePath(bipDv.path);
|
|
1126
|
-
if (!bipDv.pubkey
|
|
1214
|
+
if (!equals(bipDv.pubkey, node.publicKey)) {
|
|
1127
1215
|
throw new Error('pubkey did not match bip32Derivation');
|
|
1128
1216
|
}
|
|
1129
1217
|
return node;
|
|
@@ -1131,80 +1219,56 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1131
1219
|
}
|
|
1132
1220
|
function getSortedSigs(script, partialSig) {
|
|
1133
1221
|
const p2ms = payments.p2ms({ output: script });
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
.filter((
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
function readVarInt() {
|
|
1149
|
-
const vi = varuintDecode(buffer, offset);
|
|
1150
|
-
offset += varuintDecode.bytes;
|
|
1151
|
-
return vi;
|
|
1152
|
-
}
|
|
1153
|
-
function readVarSlice() {
|
|
1154
|
-
return readSlice(readVarInt());
|
|
1155
|
-
}
|
|
1156
|
-
function readVector() {
|
|
1157
|
-
const count = readVarInt();
|
|
1158
|
-
const vector = [];
|
|
1159
|
-
for (let i = 0; i < count; i++)
|
|
1160
|
-
vector.push(readVarSlice());
|
|
1161
|
-
return vector;
|
|
1162
|
-
}
|
|
1163
|
-
return readVector();
|
|
1164
|
-
}
|
|
1165
|
-
function sighashTypeToString(sighashType) {
|
|
1166
|
-
let text = sighashType & Transaction.SIGHASH_ANYONECANPAY ? 'SIGHASH_ANYONECANPAY | ' : '';
|
|
1167
|
-
const sigMod = sighashType & 0x1f;
|
|
1168
|
-
switch (sigMod) {
|
|
1169
|
-
case Transaction.SIGHASH_ALL:
|
|
1170
|
-
text += 'SIGHASH_ALL';
|
|
1171
|
-
break;
|
|
1172
|
-
case Transaction.SIGHASH_SINGLE:
|
|
1173
|
-
text += 'SIGHASH_SINGLE';
|
|
1174
|
-
break;
|
|
1175
|
-
case Transaction.SIGHASH_NONE:
|
|
1176
|
-
text += 'SIGHASH_NONE';
|
|
1177
|
-
break;
|
|
1178
|
-
}
|
|
1179
|
-
return text;
|
|
1222
|
+
if (!p2ms.pubkeys)
|
|
1223
|
+
throw new Error('Cannot extract pubkeys from multisig script');
|
|
1224
|
+
// for each pubkey in order of p2ms script
|
|
1225
|
+
const result = [];
|
|
1226
|
+
for (const pk of p2ms.pubkeys) {
|
|
1227
|
+
// filter partialSig array by pubkey being equal
|
|
1228
|
+
const matched = partialSig.filter((ps) => {
|
|
1229
|
+
return equals(ps.pubkey, pk);
|
|
1230
|
+
})[0];
|
|
1231
|
+
if (matched) {
|
|
1232
|
+
result.push(new Uint8Array(matched.signature));
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return result;
|
|
1180
1236
|
}
|
|
1181
1237
|
function addNonWitnessTxCache(cache, input, inputIndex) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1238
|
+
if (!input.nonWitnessUtxo)
|
|
1239
|
+
throw new Error('nonWitnessUtxo is required');
|
|
1240
|
+
// Prevent prototype pollution - ensure input is a valid object
|
|
1241
|
+
if (input === null || input === Object.prototype) {
|
|
1242
|
+
throw new Error('Invalid input object');
|
|
1243
|
+
}
|
|
1244
|
+
const nonWitnessUtxoBuf = input.nonWitnessUtxo;
|
|
1245
|
+
cache.nonWitnessUtxoBufCache[inputIndex] = nonWitnessUtxoBuf;
|
|
1246
|
+
cache.nonWitnessUtxoTxCache[inputIndex] = Transaction.fromBuffer(nonWitnessUtxoBuf);
|
|
1184
1247
|
const self = cache;
|
|
1185
1248
|
const selfIndex = inputIndex;
|
|
1186
1249
|
delete input.nonWitnessUtxo;
|
|
1187
|
-
|
|
1250
|
+
// Using Reflect.defineProperty to avoid prototype pollution concerns
|
|
1251
|
+
Reflect.defineProperty(input, 'nonWitnessUtxo', {
|
|
1188
1252
|
enumerable: true,
|
|
1189
1253
|
get() {
|
|
1190
|
-
const buf = self.
|
|
1191
|
-
const txCache = self.
|
|
1254
|
+
const buf = self.nonWitnessUtxoBufCache[selfIndex];
|
|
1255
|
+
const txCache = self.nonWitnessUtxoTxCache[selfIndex];
|
|
1192
1256
|
if (buf !== undefined) {
|
|
1193
1257
|
return buf;
|
|
1194
1258
|
}
|
|
1195
1259
|
else {
|
|
1196
1260
|
const newBuf = txCache.toBuffer();
|
|
1197
|
-
self.
|
|
1261
|
+
self.nonWitnessUtxoBufCache[selfIndex] = newBuf;
|
|
1198
1262
|
return newBuf;
|
|
1199
1263
|
}
|
|
1200
1264
|
},
|
|
1201
1265
|
set(data) {
|
|
1202
|
-
self.
|
|
1266
|
+
self.nonWitnessUtxoBufCache[selfIndex] = data;
|
|
1203
1267
|
},
|
|
1204
1268
|
});
|
|
1205
1269
|
}
|
|
1206
1270
|
function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, disableOutputChecks) {
|
|
1207
|
-
let inputAmount =
|
|
1271
|
+
let inputAmount = 0n;
|
|
1208
1272
|
inputs.forEach((input, idx) => {
|
|
1209
1273
|
if (mustFinalize && input.finalScriptSig)
|
|
1210
1274
|
tx.ins[idx].script = input.finalScriptSig;
|
|
@@ -1221,20 +1285,20 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, disableOutputChec
|
|
|
1221
1285
|
inputAmount += out.value;
|
|
1222
1286
|
}
|
|
1223
1287
|
});
|
|
1224
|
-
const outputAmount = tx.outs.reduce((total, o) => total + o.value,
|
|
1288
|
+
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
|
|
1225
1289
|
const fee = inputAmount - outputAmount;
|
|
1226
1290
|
if (!disableOutputChecks) {
|
|
1227
|
-
if (fee <
|
|
1291
|
+
if (fee < 0n) {
|
|
1228
1292
|
throw new Error(`Outputs are spending more than Inputs ${inputAmount} < ${outputAmount}`);
|
|
1229
1293
|
}
|
|
1230
1294
|
}
|
|
1231
1295
|
const bytes = tx.virtualSize();
|
|
1232
|
-
cache.
|
|
1233
|
-
cache.
|
|
1234
|
-
cache.
|
|
1296
|
+
cache.fee = Number(fee);
|
|
1297
|
+
cache.extractedTx = tx;
|
|
1298
|
+
cache.feeRate = Math.floor(Number(fee) / bytes);
|
|
1235
1299
|
}
|
|
1236
1300
|
function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
|
|
1237
|
-
const c = cache.
|
|
1301
|
+
const c = cache.nonWitnessUtxoTxCache;
|
|
1238
1302
|
if (!c[inputIndex]) {
|
|
1239
1303
|
addNonWitnessTxCache(cache, input, inputIndex);
|
|
1240
1304
|
}
|
|
@@ -1253,7 +1317,7 @@ function getScriptAndAmountFromUtxo(inputIndex, input, cache) {
|
|
|
1253
1317
|
}
|
|
1254
1318
|
else if (input.nonWitnessUtxo !== undefined) {
|
|
1255
1319
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1256
|
-
const o = nonWitnessUtxoTx.outs[cache.
|
|
1320
|
+
const o = nonWitnessUtxoTx.outs[cache.tx.ins[inputIndex].index];
|
|
1257
1321
|
return { script: o.script, value: o.value };
|
|
1258
1322
|
}
|
|
1259
1323
|
else {
|
|
@@ -1266,7 +1330,7 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) {
|
|
|
1266
1330
|
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1267
1331
|
}
|
|
1268
1332
|
function pubkeyInOutput(pubkey, output, outputIndex, cache) {
|
|
1269
|
-
const script = cache.
|
|
1333
|
+
const script = cache.tx.outs[outputIndex].script;
|
|
1270
1334
|
const { meaningfulScript } = getMeaningfulScript(script, outputIndex, 'output', output.redeemScript, output.witnessScript);
|
|
1271
1335
|
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1272
1336
|
}
|
|
@@ -1277,7 +1341,7 @@ function redeemFromFinalScriptSig(finalScript) {
|
|
|
1277
1341
|
if (!decomp)
|
|
1278
1342
|
return;
|
|
1279
1343
|
const lastItem = decomp[decomp.length - 1];
|
|
1280
|
-
if (!
|
|
1344
|
+
if (!(lastItem instanceof Uint8Array) || isPubkeyLike(lastItem) || isSigLike(lastItem))
|
|
1281
1345
|
return;
|
|
1282
1346
|
const sDecomp = bscript.decompile(lastItem);
|
|
1283
1347
|
if (!sDecomp)
|
|
@@ -1296,69 +1360,4 @@ function redeemFromFinalWitnessScript(finalScript) {
|
|
|
1296
1360
|
return;
|
|
1297
1361
|
return lastItem;
|
|
1298
1362
|
}
|
|
1299
|
-
|
|
1300
|
-
if (pubkey.length === 65) {
|
|
1301
|
-
const parity = pubkey[64] & 1;
|
|
1302
|
-
const newKey = Buffer.from(pubkey.subarray(0, 33));
|
|
1303
|
-
newKey[0] = 2 | parity;
|
|
1304
|
-
return newKey;
|
|
1305
|
-
}
|
|
1306
|
-
return Buffer.from(pubkey);
|
|
1307
|
-
}
|
|
1308
|
-
function isPubkeyLike(buf) {
|
|
1309
|
-
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
|
|
1310
|
-
}
|
|
1311
|
-
function isSigLike(buf) {
|
|
1312
|
-
return bscript.isCanonicalScriptSignature(buf);
|
|
1313
|
-
}
|
|
1314
|
-
function getMeaningfulScript(script, index, ioType, redeemScript, witnessScript) {
|
|
1315
|
-
const isP2SH = isP2SHScript(script);
|
|
1316
|
-
const isP2SHP2WSH = isP2SH && redeemScript && isP2WSHScript(redeemScript);
|
|
1317
|
-
const isP2WSH = isP2WSHScript(script);
|
|
1318
|
-
if (isP2SH && redeemScript === undefined)
|
|
1319
|
-
throw new Error('scriptPubkey is P2SH but redeemScript missing');
|
|
1320
|
-
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
|
|
1321
|
-
throw new Error('scriptPubkey or redeemScript is P2WSH but witnessScript missing');
|
|
1322
|
-
let meaningfulScript;
|
|
1323
|
-
if (isP2SHP2WSH) {
|
|
1324
|
-
meaningfulScript = witnessScript;
|
|
1325
|
-
checkRedeemScript(index, script, redeemScript, ioType);
|
|
1326
|
-
checkWitnessScript(index, redeemScript, witnessScript, ioType);
|
|
1327
|
-
checkInvalidP2WSH(meaningfulScript);
|
|
1328
|
-
}
|
|
1329
|
-
else if (isP2WSH) {
|
|
1330
|
-
meaningfulScript = witnessScript;
|
|
1331
|
-
checkWitnessScript(index, script, witnessScript, ioType);
|
|
1332
|
-
checkInvalidP2WSH(meaningfulScript);
|
|
1333
|
-
}
|
|
1334
|
-
else if (isP2SH) {
|
|
1335
|
-
meaningfulScript = redeemScript;
|
|
1336
|
-
checkRedeemScript(index, script, redeemScript, ioType);
|
|
1337
|
-
}
|
|
1338
|
-
else {
|
|
1339
|
-
meaningfulScript = script;
|
|
1340
|
-
}
|
|
1341
|
-
return {
|
|
1342
|
-
meaningfulScript,
|
|
1343
|
-
type: isP2SHP2WSH ? 'p2sh-p2wsh' : isP2SH ? 'p2sh' : isP2WSH ? 'p2wsh' : 'raw',
|
|
1344
|
-
};
|
|
1345
|
-
}
|
|
1346
|
-
function checkInvalidP2WSH(script) {
|
|
1347
|
-
if (isP2WPKH(script) || isP2SHScript(script)) {
|
|
1348
|
-
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
function classifyScript(script) {
|
|
1352
|
-
if (isP2WPKH(script))
|
|
1353
|
-
return 'witnesspubkeyhash';
|
|
1354
|
-
if (isP2PKH(script))
|
|
1355
|
-
return 'pubkeyhash';
|
|
1356
|
-
if (isP2MS(script))
|
|
1357
|
-
return 'multisig';
|
|
1358
|
-
if (isP2PK(script))
|
|
1359
|
-
return 'pubkey';
|
|
1360
|
-
return 'nonstandard';
|
|
1361
|
-
}
|
|
1362
|
-
function range(n) {
|
|
1363
|
-
return [...Array(n).keys()];
|
|
1364
|
-
}
|
|
1363
|
+
//# sourceMappingURL=psbt.js.map
|