@btc-vision/bitcoin 6.5.6 → 7.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HOW_TO_WRITE_GOOD_CODE.md +2436 -0
- package/benchmark/psbt-2000-inputs.bench.ts +178 -0
- package/benchmark/signing.bench.ts +147 -0
- package/browser/address.d.ts +57 -10
- package/browser/address.d.ts.map +1 -0
- package/browser/bech32utils.d.ts +9 -1
- package/browser/bech32utils.d.ts.map +1 -0
- package/browser/bip66.d.ts +11 -6
- package/browser/bip66.d.ts.map +1 -0
- package/browser/block.d.ts +117 -11
- package/browser/block.d.ts.map +1 -0
- package/browser/branded.d.ts +20 -0
- package/browser/branded.d.ts.map +1 -0
- package/browser/crypto/crypto.d.ts +1 -0
- package/browser/crypto/crypto.d.ts.map +1 -0
- package/browser/crypto.d.ts +46 -7
- package/browser/crypto.d.ts.map +1 -0
- package/browser/ecc/context.d.ts +129 -0
- package/browser/ecc/context.d.ts.map +1 -0
- package/browser/ecc/index.d.ts +11 -0
- package/browser/ecc/index.d.ts.map +1 -0
- package/browser/ecc/types.d.ts +128 -0
- package/browser/ecc/types.d.ts.map +1 -0
- package/browser/ecpair.d.ts +99 -0
- package/browser/errors.d.ts +124 -0
- package/browser/errors.d.ts.map +1 -0
- package/browser/index.d.ts +32 -5
- package/browser/index.d.ts.map +1 -0
- package/browser/index.js +12477 -101
- package/browser/io/BinaryReader.d.ts +276 -0
- package/browser/io/BinaryReader.d.ts.map +1 -0
- package/browser/io/BinaryWriter.d.ts +391 -0
- package/browser/io/BinaryWriter.d.ts.map +1 -0
- package/browser/io/MemoryPool.d.ts +220 -0
- package/browser/io/MemoryPool.d.ts.map +1 -0
- package/browser/io/base64.d.ts +13 -0
- package/browser/io/base64.d.ts.map +1 -0
- package/browser/io/hex.d.ts +67 -0
- package/browser/io/hex.d.ts.map +1 -0
- package/browser/io/index.d.ts +17 -0
- package/browser/io/index.d.ts.map +1 -0
- package/browser/io/utils.d.ts +199 -0
- package/browser/io/utils.d.ts.map +1 -0
- package/browser/merkle.d.ts +10 -1
- package/browser/merkle.d.ts.map +1 -0
- package/browser/networks.d.ts +70 -9
- package/browser/networks.d.ts.map +1 -0
- package/browser/opcodes.d.ts +1 -0
- package/browser/opcodes.d.ts.map +1 -0
- package/browser/payments/bip341.d.ts +35 -9
- package/browser/payments/bip341.d.ts.map +1 -0
- package/browser/payments/embed.d.ts +112 -1
- package/browser/payments/embed.d.ts.map +1 -0
- package/browser/payments/index.d.ts +17 -10
- package/browser/payments/index.d.ts.map +1 -0
- package/browser/payments/p2ms.d.ts +150 -0
- package/browser/payments/p2ms.d.ts.map +1 -0
- package/browser/payments/p2op.d.ts +150 -24
- package/browser/payments/p2op.d.ts.map +1 -0
- package/browser/payments/p2pk.d.ts +154 -1
- package/browser/payments/p2pk.d.ts.map +1 -0
- package/browser/payments/p2pkh.d.ts +176 -1
- package/browser/payments/p2pkh.d.ts.map +1 -0
- package/browser/payments/p2sh.d.ts +150 -1
- package/browser/payments/p2sh.d.ts.map +1 -0
- package/browser/payments/p2tr.d.ts +185 -1
- package/browser/payments/p2tr.d.ts.map +1 -0
- package/browser/payments/p2wpkh.d.ts +161 -1
- package/browser/payments/p2wpkh.d.ts.map +1 -0
- package/browser/payments/p2wsh.d.ts +146 -1
- package/browser/payments/p2wsh.d.ts.map +1 -0
- package/browser/payments/types.d.ts +94 -64
- package/browser/payments/types.d.ts.map +1 -0
- package/browser/psbt/bip371.d.ts +34 -8
- package/browser/psbt/bip371.d.ts.map +1 -0
- package/browser/psbt/psbtutils.d.ts +56 -16
- package/browser/psbt/psbtutils.d.ts.map +1 -0
- package/browser/psbt/types.d.ts +245 -0
- package/browser/psbt/types.d.ts.map +1 -0
- package/browser/psbt/utils.d.ts +64 -0
- package/browser/psbt/utils.d.ts.map +1 -0
- package/browser/psbt/validation.d.ts +84 -0
- package/browser/psbt/validation.d.ts.map +1 -0
- package/browser/psbt.d.ts +82 -118
- package/browser/psbt.d.ts.map +1 -0
- package/browser/pubkey.d.ts +27 -6
- package/browser/pubkey.d.ts.map +1 -0
- package/browser/push_data.d.ts +24 -2
- package/browser/push_data.d.ts.map +1 -0
- package/browser/script.d.ts +33 -8
- package/browser/script.d.ts.map +1 -0
- package/browser/script_number.d.ts +17 -0
- package/browser/script_number.d.ts.map +1 -0
- package/browser/script_signature.d.ts +23 -5
- package/browser/script_signature.d.ts.map +1 -0
- package/browser/transaction.d.ts +160 -18
- package/browser/transaction.d.ts.map +1 -0
- package/browser/types.d.ts +36 -38
- package/browser/types.d.ts.map +1 -0
- package/browser/workers/WorkerSigningPool.d.ts +143 -0
- package/browser/workers/WorkerSigningPool.d.ts.map +1 -0
- package/browser/workers/WorkerSigningPool.node.d.ts +116 -0
- package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -0
- package/browser/workers/ecc-bundle.d.ts +25 -0
- package/browser/workers/ecc-bundle.d.ts.map +1 -0
- package/browser/workers/index.d.ts +91 -0
- package/browser/workers/index.d.ts.map +1 -0
- package/browser/workers/psbt-parallel.d.ts +88 -0
- package/browser/workers/psbt-parallel.d.ts.map +1 -0
- package/browser/workers/signing-worker.d.ts +37 -0
- package/browser/workers/signing-worker.d.ts.map +1 -0
- package/browser/workers/types.d.ts +365 -0
- package/browser/workers/types.d.ts.map +1 -0
- package/build/address.d.ts +58 -11
- package/build/address.d.ts.map +1 -0
- package/build/address.js +82 -25
- package/build/address.js.map +1 -0
- package/build/bech32utils.d.ts +9 -1
- package/build/bech32utils.d.ts.map +1 -0
- package/build/bech32utils.js +10 -2
- package/build/bech32utils.js.map +1 -0
- package/build/bip66.d.ts +11 -6
- package/build/bip66.d.ts.map +1 -0
- package/build/bip66.js +32 -3
- package/build/bip66.js.map +1 -0
- package/build/block.d.ts +117 -11
- package/build/block.d.ts.map +1 -0
- package/build/block.js +202 -72
- package/build/block.js.map +1 -0
- package/build/branded.d.ts +20 -0
- package/build/branded.d.ts.map +1 -0
- package/build/branded.js +7 -0
- package/build/branded.js.map +1 -0
- package/build/crypto/crypto.d.ts +1 -0
- package/build/crypto/crypto.d.ts.map +1 -0
- package/build/crypto/crypto.js +1 -0
- package/build/crypto/crypto.js.map +1 -0
- package/build/crypto.d.ts +46 -7
- package/build/crypto.d.ts.map +1 -0
- package/build/crypto.js +65 -20
- package/build/crypto.js.map +1 -0
- package/build/ecc/context.d.ts +135 -0
- package/build/ecc/context.d.ts.map +1 -0
- package/build/ecc/context.js +232 -0
- package/build/ecc/context.js.map +1 -0
- package/build/ecc/index.d.ts +11 -0
- package/build/ecc/index.d.ts.map +1 -0
- package/build/ecc/index.js +11 -0
- package/build/ecc/index.js.map +1 -0
- package/build/ecc/types.d.ts +134 -0
- package/build/ecc/types.d.ts.map +1 -0
- package/build/ecc/types.js +8 -0
- package/build/ecc/types.js.map +1 -0
- package/build/errors.d.ts +124 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +155 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +32 -5
- package/build/index.d.ts.map +1 -0
- package/build/index.js +26 -3
- package/build/index.js.map +1 -0
- package/build/io/BinaryReader.d.ts +276 -0
- package/build/io/BinaryReader.d.ts.map +1 -0
- package/build/io/BinaryReader.js +425 -0
- package/build/io/BinaryReader.js.map +1 -0
- package/build/io/BinaryWriter.d.ts +391 -0
- package/build/io/BinaryWriter.d.ts.map +1 -0
- package/build/io/BinaryWriter.js +611 -0
- package/build/io/BinaryWriter.js.map +1 -0
- package/build/io/MemoryPool.d.ts +220 -0
- package/build/io/MemoryPool.d.ts.map +1 -0
- package/build/io/MemoryPool.js +309 -0
- package/build/io/MemoryPool.js.map +1 -0
- package/build/io/base64.d.ts +13 -0
- package/build/io/base64.d.ts.map +1 -0
- package/build/io/base64.js +20 -0
- package/build/io/base64.js.map +1 -0
- package/build/io/hex.d.ts +67 -0
- package/build/io/hex.d.ts.map +1 -0
- package/build/io/hex.js +138 -0
- package/build/io/hex.js.map +1 -0
- package/build/io/index.d.ts +17 -0
- package/build/io/index.d.ts.map +1 -0
- package/build/io/index.js +23 -0
- package/build/io/index.js.map +1 -0
- package/build/io/utils.d.ts +199 -0
- package/build/io/utils.d.ts.map +1 -0
- package/build/io/utils.js +271 -0
- package/build/io/utils.js.map +1 -0
- package/build/merkle.d.ts +10 -1
- package/build/merkle.d.ts.map +1 -0
- package/build/merkle.js +12 -1
- package/build/merkle.js.map +1 -0
- package/build/networks.d.ts +70 -9
- package/build/networks.d.ts.map +1 -0
- package/build/networks.js +90 -4
- package/build/networks.js.map +1 -0
- package/build/opcodes.d.ts +1 -0
- package/build/opcodes.d.ts.map +1 -0
- package/build/opcodes.js +1 -0
- package/build/opcodes.js.map +1 -0
- package/build/payments/bip341.d.ts +35 -9
- package/build/payments/bip341.d.ts.map +1 -0
- package/build/payments/bip341.js +34 -15
- package/build/payments/bip341.js.map +1 -0
- package/build/payments/embed.d.ts +120 -1
- package/build/payments/embed.d.ts.map +1 -0
- package/build/payments/embed.js +215 -34
- package/build/payments/embed.js.map +1 -0
- package/build/payments/index.d.ts +17 -10
- package/build/payments/index.d.ts.map +1 -0
- package/build/payments/index.js +20 -10
- package/build/payments/index.js.map +1 -0
- package/build/payments/p2ms.d.ts +159 -1
- package/build/payments/p2ms.d.ts.map +1 -0
- package/build/payments/p2ms.js +427 -108
- package/build/payments/p2ms.js.map +1 -0
- package/build/payments/p2op.d.ts +158 -24
- package/build/payments/p2op.d.ts.map +1 -0
- package/build/payments/p2op.js +379 -93
- package/build/payments/p2op.js.map +1 -0
- package/build/payments/p2pk.d.ts +162 -1
- package/build/payments/p2pk.d.ts.map +1 -0
- package/build/payments/p2pk.js +327 -58
- package/build/payments/p2pk.js.map +1 -0
- package/build/payments/p2pkh.d.ts +185 -1
- package/build/payments/p2pkh.d.ts.map +1 -0
- package/build/payments/p2pkh.js +467 -114
- package/build/payments/p2pkh.js.map +1 -0
- package/build/payments/p2sh.d.ts +159 -1
- package/build/payments/p2sh.d.ts.map +1 -0
- package/build/payments/p2sh.js +500 -150
- package/build/payments/p2sh.js.map +1 -0
- package/build/payments/p2tr.d.ts +193 -1
- package/build/payments/p2tr.d.ts.map +1 -0
- package/build/payments/p2tr.js +592 -174
- package/build/payments/p2tr.js.map +1 -0
- package/build/payments/p2wpkh.d.ts +170 -1
- package/build/payments/p2wpkh.d.ts.map +1 -0
- package/build/payments/p2wpkh.js +428 -103
- package/build/payments/p2wpkh.js.map +1 -0
- package/build/payments/p2wsh.d.ts +155 -1
- package/build/payments/p2wsh.d.ts.map +1 -0
- package/build/payments/p2wsh.js +465 -143
- package/build/payments/p2wsh.js.map +1 -0
- package/build/payments/types.d.ts +98 -64
- package/build/payments/types.d.ts.map +1 -0
- package/build/payments/types.js +17 -13
- package/build/payments/types.js.map +1 -0
- package/build/psbt/bip371.d.ts +35 -9
- package/build/psbt/bip371.d.ts.map +1 -0
- package/build/psbt/bip371.js +117 -28
- package/build/psbt/bip371.js.map +1 -0
- package/build/psbt/psbtutils.d.ts +56 -16
- package/build/psbt/psbtutils.d.ts.map +1 -0
- package/build/psbt/psbtutils.js +71 -16
- package/build/psbt/psbtutils.js.map +1 -0
- package/build/psbt/types.d.ts +249 -0
- package/build/psbt/types.d.ts.map +1 -0
- package/build/psbt/types.js +6 -0
- package/build/psbt/types.js.map +1 -0
- package/build/psbt/utils.d.ts +68 -0
- package/build/psbt/utils.d.ts.map +1 -0
- package/build/psbt/utils.js +171 -0
- package/build/psbt/utils.js.map +1 -0
- package/build/psbt/validation.d.ts +88 -0
- package/build/psbt/validation.d.ts.map +1 -0
- package/build/psbt/validation.js +149 -0
- package/build/psbt/validation.js.map +1 -0
- package/build/psbt.d.ts +84 -120
- package/build/psbt.d.ts.map +1 -0
- package/build/psbt.js +406 -413
- package/build/psbt.js.map +1 -0
- package/build/pubkey.d.ts +27 -6
- package/build/pubkey.d.ts.map +1 -0
- package/build/pubkey.js +36 -12
- package/build/pubkey.js.map +1 -0
- package/build/push_data.d.ts +24 -2
- package/build/push_data.d.ts.map +1 -0
- package/build/push_data.js +44 -12
- package/build/push_data.js.map +1 -0
- package/build/script.d.ts +33 -8
- package/build/script.d.ts.map +1 -0
- package/build/script.js +101 -37
- package/build/script.js.map +1 -0
- package/build/script_number.d.ts +17 -0
- package/build/script_number.d.ts.map +1 -0
- package/build/script_number.js +19 -0
- package/build/script_number.js.map +1 -0
- package/build/script_signature.d.ts +23 -5
- package/build/script_signature.d.ts.map +1 -0
- package/build/script_signature.js +48 -15
- package/build/script_signature.js.map +1 -0
- package/build/transaction.d.ts +160 -18
- package/build/transaction.d.ts.map +1 -0
- package/build/transaction.js +443 -176
- package/build/transaction.js.map +1 -0
- package/build/tsconfig.build.tsbuildinfo +1 -0
- package/build/types.d.ts +36 -38
- package/build/types.d.ts.map +1 -0
- package/build/types.js +169 -57
- package/build/types.js.map +1 -0
- package/build/workers/WorkerSigningPool.d.ts +174 -0
- package/build/workers/WorkerSigningPool.d.ts.map +1 -0
- package/build/workers/WorkerSigningPool.js +553 -0
- package/build/workers/WorkerSigningPool.js.map +1 -0
- package/build/workers/WorkerSigningPool.node.d.ts +124 -0
- package/build/workers/WorkerSigningPool.node.d.ts.map +1 -0
- package/build/workers/WorkerSigningPool.node.js +753 -0
- package/build/workers/WorkerSigningPool.node.js.map +1 -0
- package/build/workers/ecc-bundle.d.ts +25 -0
- package/build/workers/ecc-bundle.d.ts.map +1 -0
- package/build/workers/ecc-bundle.js +25 -0
- package/build/workers/ecc-bundle.js.map +1 -0
- package/build/workers/index.d.ts +91 -0
- package/build/workers/index.d.ts.map +1 -0
- package/build/workers/index.js +114 -0
- package/build/workers/index.js.map +1 -0
- package/build/workers/psbt-parallel.d.ts +117 -0
- package/build/workers/psbt-parallel.d.ts.map +1 -0
- package/build/workers/psbt-parallel.js +233 -0
- package/build/workers/psbt-parallel.js.map +1 -0
- package/build/workers/signing-worker.d.ts +37 -0
- package/build/workers/signing-worker.d.ts.map +1 -0
- package/build/workers/signing-worker.js +350 -0
- package/build/workers/signing-worker.js.map +1 -0
- package/build/workers/types.d.ts +365 -0
- package/build/workers/types.d.ts.map +1 -0
- package/build/workers/types.js +60 -0
- package/build/workers/types.js.map +1 -0
- package/package.json +68 -9
- package/scripts/bundle-ecc.ts +111 -0
- package/src/address.ts +91 -45
- package/src/bech32utils.ts +3 -3
- package/src/bip66.ts +34 -24
- package/src/block.ts +205 -86
- package/src/branded.ts +18 -0
- package/src/crypto.ts +64 -26
- package/src/ecc/context.ts +280 -0
- package/src/ecc/index.ts +14 -0
- package/src/ecc/types.ts +147 -0
- package/src/ecpair.d.ts +99 -0
- package/src/errors.ts +163 -0
- package/src/index.ts +112 -9
- package/src/io/BinaryReader.ts +461 -0
- package/src/io/BinaryWriter.ts +696 -0
- package/src/io/MemoryPool.ts +343 -0
- package/src/io/base64.ts +20 -0
- package/src/io/hex.ts +155 -0
- package/src/io/index.ts +41 -0
- package/src/io/utils.ts +283 -0
- package/src/merkle.ts +14 -9
- package/src/networks.ts +9 -9
- package/src/payments/bip341.ts +32 -33
- package/src/payments/embed.ts +244 -41
- package/src/payments/index.ts +12 -10
- package/src/payments/p2ms.ts +497 -118
- package/src/payments/p2op.ts +432 -134
- package/src/payments/p2pk.ts +370 -72
- package/src/payments/p2pkh.ts +524 -130
- package/src/payments/p2sh.ts +572 -169
- package/src/payments/p2tr.ts +686 -194
- package/src/payments/p2wpkh.ts +482 -105
- package/src/payments/p2wsh.ts +524 -162
- package/src/payments/types.ts +80 -66
- package/src/psbt/bip371.ts +72 -51
- package/src/psbt/psbtutils.ts +39 -40
- package/src/psbt/types.ts +324 -0
- package/src/psbt/utils.ts +188 -0
- package/src/psbt/validation.ts +185 -0
- package/src/psbt.ts +608 -827
- package/src/pubkey.ts +22 -23
- package/src/push_data.ts +18 -16
- package/src/script.ts +81 -66
- package/src/script_number.ts +6 -6
- package/src/script_signature.ts +33 -36
- package/src/transaction.ts +462 -239
- package/src/types.ts +229 -100
- package/src/workers/WorkerSigningPool.node.ts +887 -0
- package/src/workers/WorkerSigningPool.ts +666 -0
- package/src/workers/ecc-bundle.ts +26 -0
- package/src/workers/index.ts +165 -0
- package/src/workers/psbt-parallel.ts +327 -0
- package/src/workers/signing-worker.ts +353 -0
- package/src/workers/types.ts +417 -0
- package/test/address.spec.ts +9 -6
- package/test/bitcoin.core.spec.ts +16 -17
- package/test/block.spec.ts +8 -7
- package/test/bufferutils.spec.ts +228 -214
- package/test/crypto.spec.ts +19 -11
- package/test/fixtures/p2pk.json +0 -8
- package/test/fixtures/p2pkh.json +1 -1
- package/test/fixtures/p2sh.json +1 -1
- package/test/fixtures/script.json +1 -1
- package/test/fixtures/transaction.json +2 -2
- package/test/integration/_regtest.ts +25 -0
- package/test/integration/addresses.spec.ts +4 -3
- package/test/integration/bip32.spec.ts +2 -1
- package/test/integration/blocks.spec.ts +1 -1
- package/test/integration/cltv.spec.ts +18 -16
- package/test/integration/csv.spec.ts +37 -64
- package/test/integration/payments.spec.ts +5 -3
- package/test/integration/taproot.spec.ts +76 -83
- package/test/integration/transactions.spec.ts +38 -35
- package/test/payments.spec.ts +35 -13
- package/test/payments.utils.ts +17 -16
- package/test/psbt.spec.ts +111 -100
- package/test/script.spec.ts +11 -10
- package/test/script_signature.spec.ts +9 -11
- package/test/taproot-cache.spec.ts +694 -0
- package/test/transaction.spec.ts +32 -40
- package/test/types.spec.ts +74 -29
- package/test/workers-pool.spec.ts +963 -0
- package/test/workers-signing.spec.ts +635 -0
- package/test/workers.spec.ts +1390 -0
- package/tsconfig.base.json +34 -18
- package/tsconfig.browser.json +15 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +5 -14
- package/typedoc.json +29 -0
- package/vite.config.browser.ts +3 -42
- package/vitest.config.integration.ts +2 -0
- package/browser/bufferutils.d.ts +0 -34
- package/browser/chunks/crypto-BhCpKpek.js +0 -2033
- package/browser/chunks/payments-B1wlSccx.js +0 -1089
- package/browser/chunks/psbt-BCNk7JUx.js +0 -4055
- package/browser/chunks/script-DyPItFEl.js +0 -318
- package/browser/chunks/transaction-C_UbhMGn.js +0 -432
- package/browser/chunks/utils-DNZi-T5W.js +0 -761
- package/browser/ecc_lib.d.ts +0 -3
- package/browser/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/browser/hooks/HookedSigner.d.ts +0 -4
- package/browser/hooks/SignatureManager.d.ts +0 -13
- package/browser/payments/lazy.d.ts +0 -2
- package/browser/typeforce.d.ts +0 -38
- package/build/bufferutils.d.ts +0 -34
- package/build/bufferutils.js +0 -141
- package/build/ecc_lib.d.ts +0 -3
- package/build/ecc_lib.js +0 -61
- package/build/hooks/AdvancedSignatureManager.d.ts +0 -16
- package/build/hooks/AdvancedSignatureManager.js +0 -52
- package/build/hooks/HookedSigner.d.ts +0 -4
- package/build/hooks/HookedSigner.js +0 -64
- package/build/hooks/SignatureManager.d.ts +0 -13
- package/build/hooks/SignatureManager.js +0 -45
- package/build/payments/lazy.d.ts +0 -2
- package/build/payments/lazy.js +0 -28
- package/build/tsconfig.tsbuildinfo +0 -1
- package/src/bufferutils.ts +0 -188
- package/src/ecc_lib.ts +0 -94
- package/src/hooks/AdvancedSignatureManager.ts +0 -104
- package/src/hooks/HookedSigner.ts +0 -108
- package/src/hooks/SignatureManager.ts +0 -84
- package/src/payments/lazy.ts +0 -28
- package/src/typeforce.d.ts +0 -38
- package/tsconfig.webpack.json +0 -18
package/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 { checkForInput, checkForOutput, Psbt as PsbtBase } from 'bip174';
|
|
2
|
+
import { clone, equals, fromBase64, fromHex, reverse, toHex } 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,78 @@ 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 ||
|
|
174
|
-
outputData.value === undefined ||
|
|
175
|
-
(!hasAddress && !hasScript)) {
|
|
262
|
+
if (!outputData || outputData.value === undefined || (!hasAddress && !hasScript)) {
|
|
176
263
|
throw new Error(`Invalid arguments for Psbt.addOutput. ` +
|
|
177
264
|
`Requires single object with at least [script or address] and [value]`);
|
|
178
265
|
}
|
|
179
|
-
|
|
266
|
+
if (checkPartialSigs) {
|
|
267
|
+
checkInputsForPartialSig(this.data.inputs, 'addOutput', this.#cache.hasSignatures);
|
|
268
|
+
}
|
|
180
269
|
if (hasAddress) {
|
|
181
270
|
const { address } = outputData;
|
|
182
|
-
const { network } = this
|
|
271
|
+
const { network } = this.#opts;
|
|
183
272
|
const script = toOutputScript(address, network);
|
|
184
273
|
outputData = Object.assign({}, outputData, { script });
|
|
185
274
|
}
|
|
186
275
|
checkTaprootOutputFields(outputData, outputData, 'addOutput');
|
|
187
|
-
const c = this
|
|
276
|
+
const c = this.#cache;
|
|
188
277
|
this.data.addOutput(outputData);
|
|
189
|
-
c.
|
|
190
|
-
c.
|
|
191
|
-
c.
|
|
278
|
+
c.fee = undefined;
|
|
279
|
+
c.feeRate = undefined;
|
|
280
|
+
c.extractedTx = undefined;
|
|
281
|
+
c.taprootHashCache = undefined;
|
|
192
282
|
return this;
|
|
193
283
|
}
|
|
194
284
|
extractTransaction(disableFeeCheck, disableOutputChecks) {
|
|
@@ -197,43 +287,43 @@ export class Psbt {
|
|
|
197
287
|
}
|
|
198
288
|
if (!this.data.inputs.every(isFinalized))
|
|
199
289
|
throw new Error('Not finalized');
|
|
200
|
-
const c = this
|
|
290
|
+
const c = this.#cache;
|
|
201
291
|
if (!disableFeeCheck) {
|
|
202
|
-
checkFees(this, c, this
|
|
292
|
+
checkFees(this, c, this.#opts);
|
|
203
293
|
}
|
|
204
|
-
if (c.
|
|
205
|
-
return c.
|
|
206
|
-
const tx = c.
|
|
294
|
+
if (c.extractedTx)
|
|
295
|
+
return c.extractedTx;
|
|
296
|
+
const tx = c.tx.clone();
|
|
207
297
|
inputFinalizeGetAmts(this.data.inputs, tx, c, true, disableOutputChecks);
|
|
208
298
|
return tx;
|
|
209
299
|
}
|
|
210
300
|
getFeeRate(disableOutputChecks = false) {
|
|
211
|
-
return getTxCacheValue('
|
|
301
|
+
return getTxCacheValue('feeRate', 'fee rate', this.data.inputs, this.#cache, disableOutputChecks);
|
|
212
302
|
}
|
|
213
303
|
getFee(disableOutputChecks = false) {
|
|
214
|
-
return getTxCacheValue('
|
|
304
|
+
return getTxCacheValue('fee', 'fee', this.data.inputs, this.#cache, disableOutputChecks);
|
|
215
305
|
}
|
|
216
306
|
finalizeAllInputs() {
|
|
217
|
-
checkForInput(this.data.inputs, 0);
|
|
307
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
218
308
|
range(this.data.inputs.length).forEach((idx) => this.finalizeInput(idx));
|
|
219
309
|
return this;
|
|
220
310
|
}
|
|
221
311
|
finalizeInput(inputIndex, finalScriptsFunc, canRunChecks) {
|
|
222
312
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
223
313
|
if (isTaprootInput(input)) {
|
|
224
|
-
return this
|
|
314
|
+
return this.#finalizeTaprootInput(inputIndex, input, undefined, finalScriptsFunc);
|
|
225
315
|
}
|
|
226
|
-
return this
|
|
316
|
+
return this.#finalizeInput(inputIndex, input, finalScriptsFunc, canRunChecks ?? true);
|
|
227
317
|
}
|
|
228
318
|
finalizeTaprootInput(inputIndex, tapLeafHashToFinalize, finalScriptsFunc = tapScriptFinalizer) {
|
|
229
319
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
230
320
|
if (isTaprootInput(input))
|
|
231
|
-
return this
|
|
321
|
+
return this.#finalizeTaprootInput(inputIndex, input, tapLeafHashToFinalize, finalScriptsFunc);
|
|
232
322
|
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
|
|
233
323
|
}
|
|
234
324
|
getInputType(inputIndex) {
|
|
235
325
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
236
|
-
const script = getScriptFromUtxo(inputIndex, input, this
|
|
326
|
+
const script = getScriptFromUtxo(inputIndex, input, this.#cache);
|
|
237
327
|
const result = getMeaningfulScript(script, inputIndex, 'input', input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig), input.witnessScript || redeemFromFinalWitnessScript(input.finalScriptWitness));
|
|
238
328
|
const type = result.type === 'raw' ? '' : result.type + '-';
|
|
239
329
|
const mainType = classifyScript(result.meaningfulScript);
|
|
@@ -241,7 +331,7 @@ export class Psbt {
|
|
|
241
331
|
}
|
|
242
332
|
inputHasPubkey(inputIndex, pubkey) {
|
|
243
333
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
244
|
-
return pubkeyInInput(pubkey, input, inputIndex, this
|
|
334
|
+
return pubkeyInInput(pubkey, input, inputIndex, this.#cache);
|
|
245
335
|
}
|
|
246
336
|
inputHasHDKey(inputIndex, root) {
|
|
247
337
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
@@ -250,7 +340,7 @@ export class Psbt {
|
|
|
250
340
|
}
|
|
251
341
|
outputHasPubkey(outputIndex, pubkey) {
|
|
252
342
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
253
|
-
return pubkeyInOutput(pubkey, output, outputIndex, this
|
|
343
|
+
return pubkeyInOutput(pubkey, output, outputIndex, this.#cache);
|
|
254
344
|
}
|
|
255
345
|
outputHasHDKey(outputIndex, root) {
|
|
256
346
|
const output = checkForOutput(this.data.outputs, outputIndex);
|
|
@@ -258,15 +348,15 @@ export class Psbt {
|
|
|
258
348
|
return !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine);
|
|
259
349
|
}
|
|
260
350
|
validateSignaturesOfAllInputs(validator) {
|
|
261
|
-
checkForInput(this.data.inputs, 0);
|
|
351
|
+
checkForInput(this.data.inputs, 0); // making sure we have at least one
|
|
262
352
|
const results = range(this.data.inputs.length).map((idx) => this.validateSignaturesOfInput(idx, validator));
|
|
263
353
|
return results.reduce((final, res) => res && final, true);
|
|
264
354
|
}
|
|
265
355
|
validateSignaturesOfInput(inputIndex, validator, pubkey) {
|
|
266
356
|
const input = this.data.inputs[inputIndex];
|
|
267
357
|
if (isTaprootInput(input))
|
|
268
|
-
return this
|
|
269
|
-
return this
|
|
358
|
+
return this.#validateSignaturesOfTaprootInput(inputIndex, validator, pubkey);
|
|
359
|
+
return this.#validateSignaturesOfInput(inputIndex, validator, pubkey);
|
|
270
360
|
}
|
|
271
361
|
signAllInputsHD(hdKeyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
272
362
|
if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
|
|
@@ -334,6 +424,9 @@ export class Psbt {
|
|
|
334
424
|
signAllInputs(keyPair, sighashTypes) {
|
|
335
425
|
if (!keyPair || !keyPair.publicKey)
|
|
336
426
|
throw new Error('Need Signer to sign input');
|
|
427
|
+
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
428
|
+
// as input information is added, then eventually
|
|
429
|
+
// optimize this method.
|
|
337
430
|
const results = [];
|
|
338
431
|
for (const i of range(this.data.inputs.length)) {
|
|
339
432
|
try {
|
|
@@ -353,6 +446,9 @@ export class Psbt {
|
|
|
353
446
|
return new Promise((resolve, reject) => {
|
|
354
447
|
if (!keyPair || !keyPair.publicKey)
|
|
355
448
|
return reject(new Error('Need Signer to sign input'));
|
|
449
|
+
// TODO: Add a pubkey/pubkeyhash cache to each input
|
|
450
|
+
// as input information is added, then eventually
|
|
451
|
+
// optimize this method.
|
|
356
452
|
const results = [];
|
|
357
453
|
const promises = [];
|
|
358
454
|
for (const [i] of this.data.inputs.entries()) {
|
|
@@ -376,9 +472,9 @@ export class Psbt {
|
|
|
376
472
|
}
|
|
377
473
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
378
474
|
if (isTaprootInput(input)) {
|
|
379
|
-
return this
|
|
475
|
+
return this.#signTaprootInput(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
380
476
|
}
|
|
381
|
-
return this
|
|
477
|
+
return this.#signInput(inputIndex, keyPair, sighashTypes);
|
|
382
478
|
}
|
|
383
479
|
signTaprootInput(inputIndex, keyPair, tapLeafHashToSign, sighashTypes) {
|
|
384
480
|
if (!keyPair || !keyPair.publicKey) {
|
|
@@ -386,7 +482,7 @@ export class Psbt {
|
|
|
386
482
|
}
|
|
387
483
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
388
484
|
if (isTaprootInput(input)) {
|
|
389
|
-
return this
|
|
485
|
+
return this.#signTaprootInput(inputIndex, input, keyPair, tapLeafHashToSign, sighashTypes);
|
|
390
486
|
}
|
|
391
487
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
392
488
|
}
|
|
@@ -396,8 +492,8 @@ export class Psbt {
|
|
|
396
492
|
throw new Error('Need Signer to sign input');
|
|
397
493
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
398
494
|
if (isTaprootInput(input))
|
|
399
|
-
return this
|
|
400
|
-
return this
|
|
495
|
+
return this.#signTaprootInputAsync(inputIndex, input, keyPair, undefined, sighashTypes);
|
|
496
|
+
return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
|
|
401
497
|
});
|
|
402
498
|
}
|
|
403
499
|
signTaprootInputAsync(inputIndex, keyPair, tapLeafHash, sighashTypes) {
|
|
@@ -406,20 +502,20 @@ export class Psbt {
|
|
|
406
502
|
throw new Error('Need Signer to sign input');
|
|
407
503
|
const input = checkForInput(this.data.inputs, inputIndex);
|
|
408
504
|
if (isTaprootInput(input))
|
|
409
|
-
return this
|
|
505
|
+
return this.#signTaprootInputAsync(inputIndex, input, keyPair, tapLeafHash, sighashTypes);
|
|
410
506
|
throw new Error(`Input #${inputIndex} is not of type Taproot.`);
|
|
411
507
|
});
|
|
412
508
|
}
|
|
413
509
|
toBuffer() {
|
|
414
|
-
checkCache(this
|
|
415
|
-
return this.data.toBuffer();
|
|
510
|
+
checkCache(this.#cache);
|
|
511
|
+
return new Uint8Array(this.data.toBuffer());
|
|
416
512
|
}
|
|
417
513
|
toHex() {
|
|
418
|
-
checkCache(this
|
|
514
|
+
checkCache(this.#cache);
|
|
419
515
|
return this.data.toHex();
|
|
420
516
|
}
|
|
421
517
|
toBase64() {
|
|
422
|
-
checkCache(this
|
|
518
|
+
checkCache(this.#cache);
|
|
423
519
|
return this.data.toBase64();
|
|
424
520
|
}
|
|
425
521
|
updateGlobal(updateData) {
|
|
@@ -430,9 +526,21 @@ export class Psbt {
|
|
|
430
526
|
if (updateData.witnessScript)
|
|
431
527
|
checkInvalidP2WSH(updateData.witnessScript);
|
|
432
528
|
checkTaprootInputFields(this.data.inputs[inputIndex], updateData, 'updateInput');
|
|
433
|
-
|
|
529
|
+
// Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
|
|
530
|
+
const normalizedUpdate = updateData.witnessUtxo
|
|
531
|
+
? {
|
|
532
|
+
...updateData,
|
|
533
|
+
witnessUtxo: {
|
|
534
|
+
script: updateData.witnessUtxo.script,
|
|
535
|
+
value: typeof updateData.witnessUtxo.value === 'bigint'
|
|
536
|
+
? updateData.witnessUtxo.value
|
|
537
|
+
: BigInt(updateData.witnessUtxo.value),
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
: updateData;
|
|
541
|
+
this.data.updateInput(inputIndex, normalizedUpdate);
|
|
434
542
|
if (updateData.nonWitnessUtxo) {
|
|
435
|
-
addNonWitnessTxCache(this
|
|
543
|
+
addNonWitnessTxCache(this.#cache, this.data.inputs[inputIndex], inputIndex);
|
|
436
544
|
}
|
|
437
545
|
return this;
|
|
438
546
|
}
|
|
@@ -459,18 +567,18 @@ export class Psbt {
|
|
|
459
567
|
return this;
|
|
460
568
|
}
|
|
461
569
|
checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes) {
|
|
462
|
-
if (typeof keyPair.signSchnorr !== 'function')
|
|
570
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
463
571
|
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
464
|
-
const pubkey =
|
|
572
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
465
573
|
? keyPair.publicKey
|
|
466
|
-
:
|
|
467
|
-
const hashesForSig = getTaprootHashesForSig(inputIndex, input, this.data.inputs, pubkey, this
|
|
574
|
+
: new Uint8Array(keyPair.publicKey);
|
|
575
|
+
const hashesForSig = getTaprootHashesForSig(inputIndex, input, this.data.inputs, pubkey, this.#cache, tapLeafHashToSign, allowedSighashTypes);
|
|
468
576
|
if (!hashesForSig || !hashesForSig.length)
|
|
469
|
-
throw new Error(`Can not sign for input #${inputIndex} with the key ${pubkey
|
|
577
|
+
throw new Error(`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`);
|
|
470
578
|
return hashesForSig;
|
|
471
579
|
}
|
|
472
|
-
|
|
473
|
-
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(inputIndex, input, this
|
|
580
|
+
#finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts, canRunChecks = true) {
|
|
581
|
+
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(inputIndex, input, this.#cache);
|
|
474
582
|
if (!script)
|
|
475
583
|
throw new Error(`No script found for input #${inputIndex}`);
|
|
476
584
|
checkPartialSigSighashes(input);
|
|
@@ -484,9 +592,10 @@ export class Psbt {
|
|
|
484
592
|
this.data.clearFinalizedInput(inputIndex);
|
|
485
593
|
return this;
|
|
486
594
|
}
|
|
487
|
-
|
|
595
|
+
#finalizeTaprootInput(inputIndex, input, tapLeafHashToFinalize, finalScriptsFunc = tapScriptFinalizer) {
|
|
488
596
|
if (!input.witnessUtxo)
|
|
489
597
|
throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
|
|
598
|
+
// Check key spend first. Increased privacy and reduced block space.
|
|
490
599
|
if (input.tapKeySig) {
|
|
491
600
|
const payment = payments.p2tr({
|
|
492
601
|
output: input.witnessUtxo.script,
|
|
@@ -504,14 +613,14 @@ export class Psbt {
|
|
|
504
613
|
this.data.clearFinalizedInput(inputIndex);
|
|
505
614
|
return this;
|
|
506
615
|
}
|
|
507
|
-
|
|
616
|
+
#validateSignaturesOfInput(inputIndex, validator, pubkey) {
|
|
508
617
|
const input = this.data.inputs[inputIndex];
|
|
509
618
|
const partialSig = (input || {}).partialSig;
|
|
510
619
|
if (!input || !partialSig || partialSig.length < 1)
|
|
511
620
|
throw new Error('No signatures to validate');
|
|
512
621
|
if (typeof validator !== 'function')
|
|
513
622
|
throw new Error('Need validator function to validate signatures');
|
|
514
|
-
const mySigs = pubkey ? partialSig.filter((sig) => sig.pubkey
|
|
623
|
+
const mySigs = pubkey ? partialSig.filter((sig) => equals(sig.pubkey, pubkey)) : partialSig;
|
|
515
624
|
if (mySigs.length < 1)
|
|
516
625
|
throw new Error('No signatures for this pubkey');
|
|
517
626
|
const results = [];
|
|
@@ -519,21 +628,23 @@ export class Psbt {
|
|
|
519
628
|
let scriptCache;
|
|
520
629
|
let sighashCache;
|
|
521
630
|
for (const pSig of mySigs) {
|
|
522
|
-
const
|
|
631
|
+
const pSigSignature = pSig.signature;
|
|
632
|
+
const pSigPubkey = pSig.pubkey;
|
|
633
|
+
const sig = bscript.signature.decode(pSigSignature);
|
|
523
634
|
const { hash, script } = sighashCache !== sig.hashType || !hashCache || !scriptCache
|
|
524
635
|
? getHashForSig(inputIndex, Object.assign({}, input, {
|
|
525
636
|
sighashType: sig.hashType,
|
|
526
|
-
}), this
|
|
637
|
+
}), this.#cache, true)
|
|
527
638
|
: { hash: hashCache, script: scriptCache };
|
|
528
639
|
sighashCache = sig.hashType;
|
|
529
640
|
hashCache = hash;
|
|
530
641
|
scriptCache = script;
|
|
531
|
-
checkScriptForPubkey(
|
|
532
|
-
results.push(validator(
|
|
642
|
+
checkScriptForPubkey(pSigPubkey, script, 'verify');
|
|
643
|
+
results.push(validator(pSigPubkey, hash, sig.signature));
|
|
533
644
|
}
|
|
534
645
|
return results.every((res) => res);
|
|
535
646
|
}
|
|
536
|
-
validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
|
|
647
|
+
#validateSignaturesOfTaprootInput(inputIndex, validator, pubkey) {
|
|
537
648
|
const input = this.data.inputs[inputIndex];
|
|
538
649
|
const tapKeySig = (input || {}).tapKeySig;
|
|
539
650
|
const tapScriptSig = (input || {}).tapScriptSig;
|
|
@@ -541,10 +652,10 @@ export class Psbt {
|
|
|
541
652
|
throw new Error('No signatures to validate');
|
|
542
653
|
if (typeof validator !== 'function')
|
|
543
654
|
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
|
|
655
|
+
const xPubkey = pubkey ? toXOnly(pubkey) : undefined;
|
|
656
|
+
const allHashses = xPubkey
|
|
657
|
+
? getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey, this.#cache)
|
|
658
|
+
: getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this.#cache);
|
|
548
659
|
if (!allHashses.length)
|
|
549
660
|
throw new Error('No signatures for this pubkey');
|
|
550
661
|
const tapKeyHash = allHashses.find((h) => !h.leafHash);
|
|
@@ -557,9 +668,10 @@ export class Psbt {
|
|
|
557
668
|
}
|
|
558
669
|
if (tapScriptSig) {
|
|
559
670
|
for (const tapSig of tapScriptSig) {
|
|
560
|
-
const
|
|
671
|
+
const tapSigPubkey = tapSig.pubkey;
|
|
672
|
+
const tapSigHash = allHashses.find((h) => equals(tapSigPubkey, h.pubkey));
|
|
561
673
|
if (tapSigHash) {
|
|
562
|
-
const isValidTapScriptSig = validator(
|
|
674
|
+
const isValidTapScriptSig = validator(tapSigPubkey, tapSigHash.hash, trimTaprootSig(tapSig.signature));
|
|
563
675
|
if (!isValidTapScriptSig)
|
|
564
676
|
return false;
|
|
565
677
|
validationResultCount++;
|
|
@@ -568,53 +680,58 @@ export class Psbt {
|
|
|
568
680
|
}
|
|
569
681
|
return validationResultCount > 0;
|
|
570
682
|
}
|
|
571
|
-
|
|
572
|
-
const pubkey =
|
|
683
|
+
#signInput(inputIndex, keyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
684
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
573
685
|
? keyPair.publicKey
|
|
574
|
-
:
|
|
575
|
-
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this
|
|
686
|
+
: new Uint8Array(keyPair.publicKey);
|
|
687
|
+
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this.#cache, sighashTypes);
|
|
576
688
|
const sig = keyPair.sign(hash);
|
|
577
689
|
const partialSig = [
|
|
578
690
|
{
|
|
579
691
|
pubkey,
|
|
580
|
-
signature: bscript.signature.encode(
|
|
692
|
+
signature: bscript.signature.encode(sig instanceof Uint8Array ? sig : new Uint8Array(sig), sighashType),
|
|
581
693
|
},
|
|
582
694
|
];
|
|
583
695
|
this.data.updateInput(inputIndex, { partialSig });
|
|
696
|
+
this.#cache.hasSignatures = true;
|
|
584
697
|
return this;
|
|
585
698
|
}
|
|
586
|
-
|
|
587
|
-
const pubkey =
|
|
699
|
+
#signTaprootInput(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes = [Transaction.SIGHASH_DEFAULT]) {
|
|
700
|
+
const pubkey = (keyPair.publicKey instanceof Uint8Array
|
|
588
701
|
? keyPair.publicKey
|
|
589
|
-
:
|
|
702
|
+
: new Uint8Array(keyPair.publicKey));
|
|
703
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
704
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
705
|
+
// checkTaprootHashesForSig validates signSchnorr exists
|
|
590
706
|
const hashesForSig = this.checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHashToSign, allowedSighashTypes);
|
|
591
707
|
const signSchnorr = keyPair.signSchnorr.bind(keyPair);
|
|
592
|
-
const toBuffer = (data) => Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
593
708
|
const tapKeySig = hashesForSig
|
|
594
709
|
.filter((h) => !h.leafHash)
|
|
595
|
-
.map((h) => serializeTaprootSignature(
|
|
710
|
+
.map((h) => serializeTaprootSignature(signSchnorr(h.hash), input.sighashType))[0];
|
|
596
711
|
const tapScriptSig = hashesForSig
|
|
597
712
|
.filter((h) => !!h.leafHash)
|
|
598
713
|
.map((h) => ({
|
|
599
714
|
pubkey: toXOnly(pubkey),
|
|
600
|
-
signature: serializeTaprootSignature(
|
|
715
|
+
signature: serializeTaprootSignature(signSchnorr(h.hash), input.sighashType),
|
|
601
716
|
leafHash: h.leafHash,
|
|
602
717
|
}));
|
|
603
718
|
if (tapKeySig) {
|
|
604
719
|
this.data.updateInput(inputIndex, { tapKeySig });
|
|
720
|
+
this.#cache.hasSignatures = true;
|
|
605
721
|
}
|
|
606
722
|
if (tapScriptSig.length) {
|
|
607
723
|
this.data.updateInput(inputIndex, { tapScriptSig });
|
|
724
|
+
this.#cache.hasSignatures = true;
|
|
608
725
|
}
|
|
609
726
|
return this;
|
|
610
727
|
}
|
|
611
|
-
|
|
612
|
-
const pubkey =
|
|
728
|
+
#signInputAsync(inputIndex, keyPair, sighashTypes = [Transaction.SIGHASH_ALL]) {
|
|
729
|
+
const pubkey = keyPair.publicKey instanceof Uint8Array
|
|
613
730
|
? keyPair.publicKey
|
|
614
|
-
:
|
|
615
|
-
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this
|
|
731
|
+
: new Uint8Array(keyPair.publicKey);
|
|
732
|
+
const { hash, sighashType } = getHashAndSighashType(this.data.inputs, inputIndex, pubkey, this.#cache, sighashTypes);
|
|
616
733
|
return Promise.resolve(keyPair.sign(hash)).then((signature) => {
|
|
617
|
-
const sig =
|
|
734
|
+
const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
|
|
618
735
|
const partialSig = [
|
|
619
736
|
{
|
|
620
737
|
pubkey,
|
|
@@ -622,21 +739,24 @@ export class Psbt {
|
|
|
622
739
|
},
|
|
623
740
|
];
|
|
624
741
|
this.data.updateInput(inputIndex, { partialSig });
|
|
742
|
+
this.#cache.hasSignatures = true;
|
|
625
743
|
});
|
|
626
744
|
}
|
|
627
|
-
async
|
|
628
|
-
const pubkey =
|
|
745
|
+
async #signTaprootInputAsync(inputIndex, input, keyPair, tapLeafHash, sighashTypes = [Transaction.SIGHASH_DEFAULT]) {
|
|
746
|
+
const pubkey = (keyPair.publicKey instanceof Uint8Array
|
|
629
747
|
? keyPair.publicKey
|
|
630
|
-
:
|
|
748
|
+
: new Uint8Array(keyPair.publicKey));
|
|
749
|
+
if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
|
|
750
|
+
throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
|
|
751
|
+
// checkTaprootHashesForSig validates signSchnorr exists
|
|
631
752
|
const hashesForSig = this.checkTaprootHashesForSig(inputIndex, input, keyPair, tapLeafHash, sighashTypes);
|
|
632
753
|
const signSchnorr = keyPair.signSchnorr.bind(keyPair);
|
|
633
|
-
const toBuffer = (data) => Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
634
754
|
const signaturePromises = [];
|
|
635
755
|
const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
|
|
636
756
|
if (tapKeyHash) {
|
|
637
757
|
const tapKeySigPromise = Promise.resolve(signSchnorr(tapKeyHash.hash)).then((sig) => {
|
|
638
758
|
return {
|
|
639
|
-
tapKeySig: serializeTaprootSignature(
|
|
759
|
+
tapKeySig: serializeTaprootSignature(sig, input.sighashType),
|
|
640
760
|
};
|
|
641
761
|
});
|
|
642
762
|
signaturePromises.push(tapKeySigPromise);
|
|
@@ -648,7 +768,7 @@ export class Psbt {
|
|
|
648
768
|
const tapScriptSig = [
|
|
649
769
|
{
|
|
650
770
|
pubkey: toXOnly(pubkey),
|
|
651
|
-
signature: serializeTaprootSignature(
|
|
771
|
+
signature: serializeTaprootSignature(signature, input.sighashType),
|
|
652
772
|
leafHash: tsh.leafHash,
|
|
653
773
|
},
|
|
654
774
|
];
|
|
@@ -659,12 +779,23 @@ export class Psbt {
|
|
|
659
779
|
const results = await Promise.all(signaturePromises);
|
|
660
780
|
for (const v of results) {
|
|
661
781
|
this.data.updateInput(inputIndex, v);
|
|
782
|
+
this.#cache.hasSignatures = true;
|
|
662
783
|
}
|
|
663
784
|
}
|
|
664
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* This function is needed to pass to the bip174 base class's fromBuffer.
|
|
788
|
+
* It takes the "transaction buffer" portion of the psbt buffer and returns a
|
|
789
|
+
* Transaction (From the bip174 library) interface.
|
|
790
|
+
*/
|
|
665
791
|
const transactionFromBuffer = (buffer) => new PsbtTransaction(buffer);
|
|
792
|
+
/**
|
|
793
|
+
* This class implements the Transaction interface from bip174 library.
|
|
794
|
+
* It contains a bitcoinjs-lib Transaction object.
|
|
795
|
+
*/
|
|
666
796
|
class PsbtTransaction {
|
|
667
|
-
|
|
797
|
+
tx;
|
|
798
|
+
constructor(buffer = new Uint8Array([2, 0, 0, 0, 0, 0, 0, 0, 0, 0])) {
|
|
668
799
|
this.tx = Transaction.fromBuffer(buffer);
|
|
669
800
|
checkTxEmpty(this.tx);
|
|
670
801
|
Object.defineProperty(this, 'tx', {
|
|
@@ -681,20 +812,18 @@ class PsbtTransaction {
|
|
|
681
812
|
addInput(input) {
|
|
682
813
|
if (input.hash === undefined ||
|
|
683
814
|
input.index === undefined ||
|
|
684
|
-
(!
|
|
815
|
+
(!(input.hash instanceof Uint8Array) && typeof input.hash !== 'string') ||
|
|
685
816
|
typeof input.index !== 'number') {
|
|
686
817
|
throw new Error('Error adding input.');
|
|
687
818
|
}
|
|
688
|
-
const hash = typeof input.hash === 'string'
|
|
689
|
-
? reverseBuffer(Buffer.from(input.hash, 'hex'))
|
|
690
|
-
: input.hash;
|
|
819
|
+
const hash = (typeof input.hash === 'string' ? reverse(fromHex(input.hash)) : input.hash);
|
|
691
820
|
this.tx.addInput(hash, input.index, input.sequence);
|
|
692
821
|
}
|
|
693
822
|
addOutput(output) {
|
|
694
823
|
if (output.script === undefined ||
|
|
695
824
|
output.value === undefined ||
|
|
696
|
-
!
|
|
697
|
-
typeof output.value !== '
|
|
825
|
+
!(output.script instanceof Uint8Array) ||
|
|
826
|
+
typeof output.value !== 'bigint') {
|
|
698
827
|
throw new Error('Error adding output.');
|
|
699
828
|
}
|
|
700
829
|
this.tx.addOutput(output.script, output.value);
|
|
@@ -713,6 +842,8 @@ function canFinalize(input, script, scriptType) {
|
|
|
713
842
|
const p2ms = payments.p2ms({
|
|
714
843
|
output: script,
|
|
715
844
|
});
|
|
845
|
+
if (p2ms.m === undefined)
|
|
846
|
+
throw new Error('Cannot determine m for multisig');
|
|
716
847
|
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
|
|
717
848
|
}
|
|
718
849
|
case 'nonstandard':
|
|
@@ -721,11 +852,6 @@ function canFinalize(input, script, scriptType) {
|
|
|
721
852
|
return false;
|
|
722
853
|
}
|
|
723
854
|
}
|
|
724
|
-
function checkCache(cache) {
|
|
725
|
-
if (cache.__UNSAFE_SIGN_NONSEGWIT) {
|
|
726
|
-
throw new Error('Not BIP174 compliant, can not export');
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
855
|
function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
730
856
|
if (!partialSig)
|
|
731
857
|
return false;
|
|
@@ -734,7 +860,7 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
|
734
860
|
sigs = pubkeys
|
|
735
861
|
.map((pkey) => {
|
|
736
862
|
const pubkey = compressPubkey(pkey);
|
|
737
|
-
return partialSig.find((pSig) => pSig.pubkey
|
|
863
|
+
return partialSig.find((pSig) => equals(pSig.pubkey, pubkey));
|
|
738
864
|
})
|
|
739
865
|
.filter((v) => !!v);
|
|
740
866
|
}
|
|
@@ -745,31 +871,25 @@ function hasSigs(neededSigs, partialSig, pubkeys) {
|
|
|
745
871
|
throw new Error('Too many signatures');
|
|
746
872
|
return sigs.length === neededSigs;
|
|
747
873
|
}
|
|
748
|
-
function isFinalized(input) {
|
|
749
|
-
return !!input.finalScriptSig || !!input.finalScriptWitness;
|
|
750
|
-
}
|
|
751
874
|
function bip32DerivationIsMine(root) {
|
|
752
875
|
return (d) => {
|
|
753
|
-
const fingerprint =
|
|
876
|
+
const fingerprint = root.fingerprint instanceof Uint8Array
|
|
754
877
|
? root.fingerprint
|
|
755
|
-
:
|
|
756
|
-
if (!d.masterFingerprint
|
|
878
|
+
: new Uint8Array(root.fingerprint);
|
|
879
|
+
if (!equals(d.masterFingerprint, fingerprint))
|
|
757
880
|
return false;
|
|
758
881
|
const derivedPubkey = root.derivePath(d.path).publicKey;
|
|
759
|
-
const pubkey =
|
|
760
|
-
if (!
|
|
882
|
+
const pubkey = derivedPubkey instanceof Uint8Array ? derivedPubkey : new Uint8Array(derivedPubkey);
|
|
883
|
+
if (!equals(pubkey, d.pubkey))
|
|
761
884
|
return false;
|
|
762
885
|
return true;
|
|
763
886
|
};
|
|
764
887
|
}
|
|
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
888
|
function checkFees(psbt, cache, opts) {
|
|
771
|
-
const feeRate = cache.
|
|
772
|
-
|
|
889
|
+
const feeRate = cache.feeRate || psbt.getFeeRate();
|
|
890
|
+
if (!cache.extractedTx)
|
|
891
|
+
throw new Error('Transaction not extracted');
|
|
892
|
+
const vsize = cache.extractedTx.virtualSize();
|
|
773
893
|
const satoshis = feeRate * vsize;
|
|
774
894
|
if (feeRate >= opts.maximumFeeRate) {
|
|
775
895
|
throw new Error(`Warning: You are paying around ${(satoshis / 1e8).toFixed(8)} in ` +
|
|
@@ -779,81 +899,24 @@ function checkFees(psbt, cache, opts) {
|
|
|
779
899
|
`pass true to the first arg of extractTransaction.`);
|
|
780
900
|
}
|
|
781
901
|
}
|
|
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
902
|
function getTxCacheValue(key, name, inputs, c, disableOutputChecks = false) {
|
|
840
903
|
if (!inputs.every(isFinalized))
|
|
841
904
|
throw new Error(`PSBT must be finalized to calculate ${name}`);
|
|
842
|
-
if (key === '
|
|
843
|
-
return c.
|
|
844
|
-
if (key === '
|
|
845
|
-
return c.
|
|
905
|
+
if (key === 'feeRate' && c.feeRate)
|
|
906
|
+
return c.feeRate;
|
|
907
|
+
if (key === 'fee' && c.fee)
|
|
908
|
+
return c.fee;
|
|
846
909
|
let tx;
|
|
847
910
|
let mustFinalize = true;
|
|
848
|
-
if (c.
|
|
849
|
-
tx = c.
|
|
911
|
+
if (c.extractedTx) {
|
|
912
|
+
tx = c.extractedTx;
|
|
850
913
|
mustFinalize = false;
|
|
851
914
|
}
|
|
852
915
|
else {
|
|
853
|
-
tx = c.
|
|
916
|
+
tx = c.tx.clone();
|
|
854
917
|
}
|
|
855
918
|
inputFinalizeGetAmts(inputs, tx, c, mustFinalize, disableOutputChecks);
|
|
856
|
-
const value = key === '
|
|
919
|
+
const value = key === 'feeRate' ? c.feeRate : c.fee;
|
|
857
920
|
if (value === undefined)
|
|
858
921
|
throw new Error(`Failed to calculate ${name}`);
|
|
859
922
|
return value;
|
|
@@ -863,23 +926,27 @@ export function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP
|
|
|
863
926
|
if (!canFinalize(input, script, scriptType) && canRunChecks) {
|
|
864
927
|
throw new Error(`Can not finalize input #${inputIndex}`);
|
|
865
928
|
}
|
|
929
|
+
if (!input.partialSig)
|
|
930
|
+
throw new Error('Input missing partial signatures');
|
|
866
931
|
return prepareFinalScripts(script, scriptType, input.partialSig, isSegwit, isP2SH, isP2WSH, solution);
|
|
867
932
|
}
|
|
868
933
|
export function prepareFinalScripts(script, scriptType, partialSig, isSegwit, isP2SH, isP2WSH, solution) {
|
|
869
934
|
let finalScriptSig;
|
|
870
935
|
let finalScriptWitness;
|
|
936
|
+
// Wow, the payments API is very handy
|
|
871
937
|
const payment = getPayment(script, scriptType, partialSig);
|
|
872
938
|
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
|
|
873
939
|
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
|
|
874
940
|
if (isSegwit) {
|
|
875
|
-
if (p2wsh) {
|
|
941
|
+
if (p2wsh && p2wsh.witness) {
|
|
876
942
|
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
|
|
877
943
|
}
|
|
878
|
-
else if (payment) {
|
|
944
|
+
else if (payment && payment.witness) {
|
|
879
945
|
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
|
|
880
946
|
}
|
|
881
947
|
else {
|
|
882
|
-
|
|
948
|
+
// nonstandard segwit script
|
|
949
|
+
finalScriptWitness = witnessStackToScriptWitness(solution ?? [new Uint8Array([0x00])]);
|
|
883
950
|
}
|
|
884
951
|
if (p2sh) {
|
|
885
952
|
finalScriptSig = p2sh?.input;
|
|
@@ -891,8 +958,7 @@ export function prepareFinalScripts(script, scriptType, partialSig, isSegwit, is
|
|
|
891
958
|
}
|
|
892
959
|
else {
|
|
893
960
|
if (!payment) {
|
|
894
|
-
finalScriptSig =
|
|
895
|
-
Array.isArray(solution) && solution[0] ? solution[0] : Buffer.from([0x01]);
|
|
961
|
+
finalScriptSig = (Array.isArray(solution) && solution[0] ? solution[0] : new Uint8Array([0x01]));
|
|
896
962
|
}
|
|
897
963
|
else {
|
|
898
964
|
finalScriptSig = payment.input;
|
|
@@ -914,7 +980,7 @@ function getHashAndSighashType(inputs, inputIndex, pubkey, cache, sighashTypes)
|
|
|
914
980
|
};
|
|
915
981
|
}
|
|
916
982
|
function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
917
|
-
const unsignedTx = cache.
|
|
983
|
+
const unsignedTx = cache.tx;
|
|
918
984
|
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
|
919
985
|
checkSighashTypeAllowed(sighashType, sighashTypes);
|
|
920
986
|
let hash;
|
|
@@ -923,33 +989,42 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
923
989
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
924
990
|
const prevoutHash = unsignedTx.ins[inputIndex].hash;
|
|
925
991
|
const utxoHash = nonWitnessUtxoTx.getHash();
|
|
926
|
-
|
|
992
|
+
// If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
|
|
993
|
+
if (!equals(prevoutHash, utxoHash)) {
|
|
927
994
|
throw new Error(`Non-witness UTXO hash for input #${inputIndex} doesn't match the hash specified in the prevout`);
|
|
928
995
|
}
|
|
929
996
|
const prevoutIndex = unsignedTx.ins[inputIndex].index;
|
|
930
997
|
prevout = nonWitnessUtxoTx.outs[prevoutIndex];
|
|
931
998
|
}
|
|
932
999
|
else if (input.witnessUtxo) {
|
|
933
|
-
prevout =
|
|
1000
|
+
prevout = {
|
|
1001
|
+
script: input.witnessUtxo.script,
|
|
1002
|
+
value: input.witnessUtxo.value,
|
|
1003
|
+
};
|
|
934
1004
|
}
|
|
935
1005
|
else {
|
|
936
1006
|
throw new Error('Need a Utxo input item for signing');
|
|
937
1007
|
}
|
|
938
1008
|
const { meaningfulScript, type } = getMeaningfulScript(prevout.script, inputIndex, 'input', input.redeemScript, input.witnessScript);
|
|
1009
|
+
const script = meaningfulScript;
|
|
939
1010
|
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
|
|
940
|
-
hash = unsignedTx.hashForWitnessV0(inputIndex,
|
|
1011
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, script, prevout.value, sighashType);
|
|
941
1012
|
}
|
|
942
1013
|
else if (isP2WPKH(meaningfulScript)) {
|
|
943
|
-
|
|
1014
|
+
// P2WPKH uses the P2PKH template for prevoutScript when signing
|
|
1015
|
+
const p2pkhPayment = payments.p2pkh({
|
|
944
1016
|
hash: meaningfulScript.subarray(2),
|
|
945
|
-
})
|
|
946
|
-
|
|
1017
|
+
});
|
|
1018
|
+
if (!p2pkhPayment.output)
|
|
1019
|
+
throw new Error('Unable to create signing script');
|
|
1020
|
+
hash = unsignedTx.hashForWitnessV0(inputIndex, p2pkhPayment.output, prevout.value, sighashType);
|
|
947
1021
|
}
|
|
948
1022
|
else {
|
|
949
|
-
|
|
1023
|
+
// non-segwit
|
|
1024
|
+
if (input.nonWitnessUtxo === undefined && !cache.unsafeSignNonSegwit)
|
|
950
1025
|
throw new Error(`Input #${inputIndex} has witnessUtxo but non-segwit script: ` +
|
|
951
|
-
meaningfulScript
|
|
952
|
-
if (!forValidate && cache.
|
|
1026
|
+
toHex(meaningfulScript));
|
|
1027
|
+
if (!forValidate && cache.unsafeSignNonSegwit)
|
|
953
1028
|
console.warn('Warning: Signing non-segwit inputs without the full parent transaction ' +
|
|
954
1029
|
'means there is a chance that a miner could feed you incorrect information ' +
|
|
955
1030
|
"to trick you into paying large fees. This behavior is the same as Psbt's predecessor " +
|
|
@@ -957,10 +1032,10 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
|
|
|
957
1032
|
'able to export this Psbt with toBuffer|toBase64|toHex since it is not ' +
|
|
958
1033
|
'BIP174 compliant.\n*********************\nPROCEED WITH CAUTION!\n' +
|
|
959
1034
|
'*********************');
|
|
960
|
-
hash = unsignedTx.hashForSignature(inputIndex,
|
|
1035
|
+
hash = unsignedTx.hashForSignature(inputIndex, script, sighashType);
|
|
961
1036
|
}
|
|
962
1037
|
return {
|
|
963
|
-
script
|
|
1038
|
+
script,
|
|
964
1039
|
sighashType,
|
|
965
1040
|
hash,
|
|
966
1041
|
};
|
|
@@ -982,24 +1057,34 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) {
|
|
|
982
1057
|
}
|
|
983
1058
|
function getPrevoutTaprootKey(inputIndex, input, cache) {
|
|
984
1059
|
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
|
|
985
|
-
return isP2TR(script) ?
|
|
1060
|
+
return isP2TR(script) ? script.subarray(2, 34) : null;
|
|
986
1061
|
}
|
|
987
1062
|
function trimTaprootSig(signature) {
|
|
988
|
-
return signature.length === 64 ? signature :
|
|
1063
|
+
return signature.length === 64 ? signature : signature.subarray(0, 64);
|
|
989
1064
|
}
|
|
990
1065
|
function getTaprootHashesForSig(inputIndex, input, inputs, pubkey, cache, tapLeafHashToSign, allowedSighashTypes) {
|
|
991
|
-
const unsignedTx = cache.
|
|
1066
|
+
const unsignedTx = cache.tx;
|
|
992
1067
|
const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT;
|
|
993
1068
|
checkSighashTypeAllowed(sighashType, allowedSighashTypes);
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1069
|
+
if (!cache.prevOuts) {
|
|
1070
|
+
const prevOuts = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache));
|
|
1071
|
+
cache.prevOuts = prevOuts;
|
|
1072
|
+
cache.signingScripts = prevOuts.map((o) => o.script);
|
|
1073
|
+
cache.values = prevOuts.map((o) => o.value);
|
|
1074
|
+
}
|
|
1075
|
+
const signingScripts = cache.signingScripts;
|
|
1076
|
+
const values = cache.values;
|
|
1077
|
+
// Compute taproot hash cache once for all inputs (O(n) -> O(1) per input)
|
|
1078
|
+
if (!cache.taprootHashCache) {
|
|
1079
|
+
cache.taprootHashCache = unsignedTx.getTaprootHashCache(signingScripts, values);
|
|
1080
|
+
}
|
|
1081
|
+
const taprootCache = cache.taprootHashCache;
|
|
997
1082
|
const hashes = [];
|
|
998
1083
|
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 });
|
|
1084
|
+
const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || new Uint8Array(0);
|
|
1085
|
+
if (equals(toXOnly(pubkey), outputKey)) {
|
|
1086
|
+
const tapKeyHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, undefined, undefined, taprootCache);
|
|
1087
|
+
hashes.push({ pubkey: pubkey, hash: tapKeyHash });
|
|
1003
1088
|
}
|
|
1004
1089
|
}
|
|
1005
1090
|
const tapLeafHashes = (input.tapLeafScript || [])
|
|
@@ -1011,11 +1096,11 @@ function getTaprootHashesForSig(inputIndex, input, inputs, pubkey, cache, tapLea
|
|
|
1011
1096
|
});
|
|
1012
1097
|
return Object.assign({ hash }, tapLeaf);
|
|
1013
1098
|
})
|
|
1014
|
-
.filter((tapLeaf) => !tapLeafHashToSign ||
|
|
1099
|
+
.filter((tapLeaf) => !tapLeafHashToSign || equals(tapLeafHashToSign, tapLeaf.hash))
|
|
1015
1100
|
.map((tapLeaf) => {
|
|
1016
|
-
const tapScriptHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, tapLeaf.hash);
|
|
1101
|
+
const tapScriptHash = unsignedTx.hashForWitnessV1(inputIndex, signingScripts, values, sighashType, tapLeaf.hash, undefined, taprootCache);
|
|
1017
1102
|
return {
|
|
1018
|
-
pubkey,
|
|
1103
|
+
pubkey: pubkey,
|
|
1019
1104
|
hash: tapScriptHash,
|
|
1020
1105
|
leafHash: tapLeaf.hash,
|
|
1021
1106
|
};
|
|
@@ -1030,41 +1115,38 @@ function checkSighashTypeAllowed(sighashType, sighashTypes) {
|
|
|
1030
1115
|
}
|
|
1031
1116
|
}
|
|
1032
1117
|
function getPayment(script, scriptType, partialSig) {
|
|
1033
|
-
|
|
1118
|
+
const scriptBranded = script;
|
|
1034
1119
|
switch (scriptType) {
|
|
1035
1120
|
case 'multisig': {
|
|
1036
1121
|
const sigs = getSortedSigs(script, partialSig);
|
|
1037
|
-
|
|
1038
|
-
output:
|
|
1122
|
+
return payments.p2ms({
|
|
1123
|
+
output: scriptBranded,
|
|
1039
1124
|
signatures: sigs,
|
|
1040
1125
|
});
|
|
1041
|
-
break;
|
|
1042
1126
|
}
|
|
1043
1127
|
case 'pubkey':
|
|
1044
|
-
|
|
1045
|
-
output:
|
|
1128
|
+
return payments.p2pk({
|
|
1129
|
+
output: scriptBranded,
|
|
1046
1130
|
signature: partialSig[0].signature,
|
|
1047
1131
|
});
|
|
1048
|
-
break;
|
|
1049
1132
|
case 'pubkeyhash':
|
|
1050
|
-
|
|
1051
|
-
output:
|
|
1133
|
+
return payments.p2pkh({
|
|
1134
|
+
output: scriptBranded,
|
|
1052
1135
|
pubkey: partialSig[0].pubkey,
|
|
1053
1136
|
signature: partialSig[0].signature,
|
|
1054
1137
|
});
|
|
1055
|
-
break;
|
|
1056
1138
|
case 'witnesspubkeyhash':
|
|
1057
|
-
|
|
1058
|
-
output:
|
|
1139
|
+
return payments.p2wpkh({
|
|
1140
|
+
output: scriptBranded,
|
|
1059
1141
|
pubkey: partialSig[0].pubkey,
|
|
1060
1142
|
signature: partialSig[0].signature,
|
|
1061
1143
|
});
|
|
1062
|
-
|
|
1144
|
+
default:
|
|
1145
|
+
throw new Error(`Unknown script type: ${scriptType}`);
|
|
1063
1146
|
}
|
|
1064
|
-
return payment;
|
|
1065
1147
|
}
|
|
1066
1148
|
function getScriptFromInput(inputIndex, input, cache) {
|
|
1067
|
-
const unsignedTx = cache.
|
|
1149
|
+
const unsignedTx = cache.tx;
|
|
1068
1150
|
const res = {
|
|
1069
1151
|
script: null,
|
|
1070
1152
|
isSegwit: false,
|
|
@@ -1089,7 +1171,7 @@ function getScriptFromInput(inputIndex, input, cache) {
|
|
|
1089
1171
|
res.script = input.witnessUtxo.script;
|
|
1090
1172
|
}
|
|
1091
1173
|
}
|
|
1092
|
-
if (input.witnessScript || isP2WPKH(res.script)) {
|
|
1174
|
+
if (input.witnessScript || (res.script && isP2WPKH(res.script))) {
|
|
1093
1175
|
res.isSegwit = true;
|
|
1094
1176
|
}
|
|
1095
1177
|
else {
|
|
@@ -1110,7 +1192,7 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1110
1192
|
}
|
|
1111
1193
|
const myDerivations = input.bip32Derivation
|
|
1112
1194
|
.map((bipDv) => {
|
|
1113
|
-
if (bipDv.masterFingerprint
|
|
1195
|
+
if (equals(bipDv.masterFingerprint, hdKeyPair.fingerprint)) {
|
|
1114
1196
|
return bipDv;
|
|
1115
1197
|
}
|
|
1116
1198
|
else {
|
|
@@ -1123,7 +1205,7 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1123
1205
|
}
|
|
1124
1206
|
return myDerivations.map((bipDv) => {
|
|
1125
1207
|
const node = hdKeyPair.derivePath(bipDv.path);
|
|
1126
|
-
if (!bipDv.pubkey
|
|
1208
|
+
if (!equals(bipDv.pubkey, node.publicKey)) {
|
|
1127
1209
|
throw new Error('pubkey did not match bip32Derivation');
|
|
1128
1210
|
}
|
|
1129
1211
|
return node;
|
|
@@ -1131,80 +1213,56 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
|
|
|
1131
1213
|
}
|
|
1132
1214
|
function getSortedSigs(script, partialSig) {
|
|
1133
1215
|
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;
|
|
1216
|
+
if (!p2ms.pubkeys)
|
|
1217
|
+
throw new Error('Cannot extract pubkeys from multisig script');
|
|
1218
|
+
// for each pubkey in order of p2ms script
|
|
1219
|
+
const result = [];
|
|
1220
|
+
for (const pk of p2ms.pubkeys) {
|
|
1221
|
+
// filter partialSig array by pubkey being equal
|
|
1222
|
+
const matched = partialSig.filter((ps) => {
|
|
1223
|
+
return equals(ps.pubkey, pk);
|
|
1224
|
+
})[0];
|
|
1225
|
+
if (matched) {
|
|
1226
|
+
result.push(new Uint8Array(matched.signature));
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return result;
|
|
1180
1230
|
}
|
|
1181
1231
|
function addNonWitnessTxCache(cache, input, inputIndex) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1232
|
+
if (!input.nonWitnessUtxo)
|
|
1233
|
+
throw new Error('nonWitnessUtxo is required');
|
|
1234
|
+
// Prevent prototype pollution - ensure input is a valid object
|
|
1235
|
+
if (input === null || input === Object.prototype) {
|
|
1236
|
+
throw new Error('Invalid input object');
|
|
1237
|
+
}
|
|
1238
|
+
const nonWitnessUtxoBuf = input.nonWitnessUtxo;
|
|
1239
|
+
cache.nonWitnessUtxoBufCache[inputIndex] = nonWitnessUtxoBuf;
|
|
1240
|
+
cache.nonWitnessUtxoTxCache[inputIndex] = Transaction.fromBuffer(nonWitnessUtxoBuf);
|
|
1184
1241
|
const self = cache;
|
|
1185
1242
|
const selfIndex = inputIndex;
|
|
1186
1243
|
delete input.nonWitnessUtxo;
|
|
1187
|
-
|
|
1244
|
+
// Using Reflect.defineProperty to avoid prototype pollution concerns
|
|
1245
|
+
Reflect.defineProperty(input, 'nonWitnessUtxo', {
|
|
1188
1246
|
enumerable: true,
|
|
1189
1247
|
get() {
|
|
1190
|
-
const buf = self.
|
|
1191
|
-
const txCache = self.
|
|
1248
|
+
const buf = self.nonWitnessUtxoBufCache[selfIndex];
|
|
1249
|
+
const txCache = self.nonWitnessUtxoTxCache[selfIndex];
|
|
1192
1250
|
if (buf !== undefined) {
|
|
1193
1251
|
return buf;
|
|
1194
1252
|
}
|
|
1195
1253
|
else {
|
|
1196
1254
|
const newBuf = txCache.toBuffer();
|
|
1197
|
-
self.
|
|
1255
|
+
self.nonWitnessUtxoBufCache[selfIndex] = newBuf;
|
|
1198
1256
|
return newBuf;
|
|
1199
1257
|
}
|
|
1200
1258
|
},
|
|
1201
1259
|
set(data) {
|
|
1202
|
-
self.
|
|
1260
|
+
self.nonWitnessUtxoBufCache[selfIndex] = data;
|
|
1203
1261
|
},
|
|
1204
1262
|
});
|
|
1205
1263
|
}
|
|
1206
1264
|
function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, disableOutputChecks) {
|
|
1207
|
-
let inputAmount =
|
|
1265
|
+
let inputAmount = 0n;
|
|
1208
1266
|
inputs.forEach((input, idx) => {
|
|
1209
1267
|
if (mustFinalize && input.finalScriptSig)
|
|
1210
1268
|
tx.ins[idx].script = input.finalScriptSig;
|
|
@@ -1221,20 +1279,20 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize, disableOutputChec
|
|
|
1221
1279
|
inputAmount += out.value;
|
|
1222
1280
|
}
|
|
1223
1281
|
});
|
|
1224
|
-
const outputAmount = tx.outs.reduce((total, o) => total + o.value,
|
|
1282
|
+
const outputAmount = tx.outs.reduce((total, o) => total + o.value, 0n);
|
|
1225
1283
|
const fee = inputAmount - outputAmount;
|
|
1226
1284
|
if (!disableOutputChecks) {
|
|
1227
|
-
if (fee <
|
|
1285
|
+
if (fee < 0n) {
|
|
1228
1286
|
throw new Error(`Outputs are spending more than Inputs ${inputAmount} < ${outputAmount}`);
|
|
1229
1287
|
}
|
|
1230
1288
|
}
|
|
1231
1289
|
const bytes = tx.virtualSize();
|
|
1232
|
-
cache.
|
|
1233
|
-
cache.
|
|
1234
|
-
cache.
|
|
1290
|
+
cache.fee = Number(fee);
|
|
1291
|
+
cache.extractedTx = tx;
|
|
1292
|
+
cache.feeRate = Math.floor(Number(fee) / bytes);
|
|
1235
1293
|
}
|
|
1236
1294
|
function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
|
|
1237
|
-
const c = cache.
|
|
1295
|
+
const c = cache.nonWitnessUtxoTxCache;
|
|
1238
1296
|
if (!c[inputIndex]) {
|
|
1239
1297
|
addNonWitnessTxCache(cache, input, inputIndex);
|
|
1240
1298
|
}
|
|
@@ -1253,7 +1311,7 @@ function getScriptAndAmountFromUtxo(inputIndex, input, cache) {
|
|
|
1253
1311
|
}
|
|
1254
1312
|
else if (input.nonWitnessUtxo !== undefined) {
|
|
1255
1313
|
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache(cache, input, inputIndex);
|
|
1256
|
-
const o = nonWitnessUtxoTx.outs[cache.
|
|
1314
|
+
const o = nonWitnessUtxoTx.outs[cache.tx.ins[inputIndex].index];
|
|
1257
1315
|
return { script: o.script, value: o.value };
|
|
1258
1316
|
}
|
|
1259
1317
|
else {
|
|
@@ -1266,7 +1324,7 @@ function pubkeyInInput(pubkey, input, inputIndex, cache) {
|
|
|
1266
1324
|
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1267
1325
|
}
|
|
1268
1326
|
function pubkeyInOutput(pubkey, output, outputIndex, cache) {
|
|
1269
|
-
const script = cache.
|
|
1327
|
+
const script = cache.tx.outs[outputIndex].script;
|
|
1270
1328
|
const { meaningfulScript } = getMeaningfulScript(script, outputIndex, 'output', output.redeemScript, output.witnessScript);
|
|
1271
1329
|
return pubkeyInScript(pubkey, meaningfulScript);
|
|
1272
1330
|
}
|
|
@@ -1277,7 +1335,7 @@ function redeemFromFinalScriptSig(finalScript) {
|
|
|
1277
1335
|
if (!decomp)
|
|
1278
1336
|
return;
|
|
1279
1337
|
const lastItem = decomp[decomp.length - 1];
|
|
1280
|
-
if (!
|
|
1338
|
+
if (!(lastItem instanceof Uint8Array) || isPubkeyLike(lastItem) || isSigLike(lastItem))
|
|
1281
1339
|
return;
|
|
1282
1340
|
const sDecomp = bscript.decompile(lastItem);
|
|
1283
1341
|
if (!sDecomp)
|
|
@@ -1296,69 +1354,4 @@ function redeemFromFinalWitnessScript(finalScript) {
|
|
|
1296
1354
|
return;
|
|
1297
1355
|
return lastItem;
|
|
1298
1356
|
}
|
|
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
|
-
}
|
|
1357
|
+
//# sourceMappingURL=psbt.js.map
|