@btc-vision/bitcoin 7.0.0-alpha.0 → 7.0.0-alpha.2

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