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

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 (369) hide show
  1. package/README.md +455 -155
  2. package/browser/address.d.ts +6 -2
  3. package/browser/address.d.ts.map +1 -1
  4. package/browser/block.d.ts.map +1 -1
  5. package/browser/branded.d.ts +3 -14
  6. package/browser/branded.d.ts.map +1 -1
  7. package/browser/chunks/psbt-parallel-BBFlkmiv.js +10717 -0
  8. package/browser/crypto.d.ts +1 -1
  9. package/browser/ecc/context.d.ts +25 -24
  10. package/browser/ecc/context.d.ts.map +1 -1
  11. package/browser/ecc/index.d.ts +1 -1
  12. package/browser/ecc/index.d.ts.map +1 -1
  13. package/browser/ecc/types.d.ts +10 -123
  14. package/browser/ecc/types.d.ts.map +1 -1
  15. package/browser/env.d.ts +13 -0
  16. package/browser/env.d.ts.map +1 -0
  17. package/browser/index.d.ts +7 -7
  18. package/browser/index.d.ts.map +1 -1
  19. package/browser/index.js +2497 -11686
  20. package/browser/io/BinaryReader.d.ts +15 -15
  21. package/browser/io/BinaryReader.d.ts.map +1 -1
  22. package/browser/io/BinaryWriter.d.ts +17 -17
  23. package/browser/io/BinaryWriter.d.ts.map +1 -1
  24. package/browser/io/hex.d.ts.map +1 -1
  25. package/browser/io/index.d.ts +0 -1
  26. package/browser/io/index.d.ts.map +1 -1
  27. package/browser/opcodes.d.ts +11 -0
  28. package/browser/opcodes.d.ts.map +1 -1
  29. package/browser/payments/bip341.d.ts +1 -1
  30. package/browser/payments/bip341.d.ts.map +1 -1
  31. package/browser/payments/embed.d.ts +1 -1
  32. package/browser/payments/embed.d.ts.map +1 -1
  33. package/browser/payments/p2ms.d.ts.map +1 -1
  34. package/browser/payments/p2op.d.ts +1 -1
  35. package/browser/payments/p2op.d.ts.map +1 -1
  36. package/browser/payments/p2pk.d.ts +1 -1
  37. package/browser/payments/p2pk.d.ts.map +1 -1
  38. package/browser/payments/p2pkh.d.ts +1 -1
  39. package/browser/payments/p2pkh.d.ts.map +1 -1
  40. package/browser/payments/p2sh.d.ts.map +1 -1
  41. package/browser/payments/p2tr.d.ts +2 -2
  42. package/browser/payments/p2tr.d.ts.map +1 -1
  43. package/browser/payments/p2wpkh.d.ts +1 -1
  44. package/browser/payments/p2wpkh.d.ts.map +1 -1
  45. package/browser/payments/p2wsh.d.ts.map +1 -1
  46. package/browser/payments/types.d.ts +1 -1
  47. package/browser/payments/types.d.ts.map +1 -1
  48. package/browser/psbt/PsbtCache.d.ts +54 -0
  49. package/browser/psbt/PsbtCache.d.ts.map +1 -0
  50. package/browser/psbt/PsbtFinalizer.d.ts +21 -0
  51. package/browser/psbt/PsbtFinalizer.d.ts.map +1 -0
  52. package/browser/psbt/PsbtSigner.d.ts +32 -0
  53. package/browser/psbt/PsbtSigner.d.ts.map +1 -0
  54. package/browser/psbt/PsbtTransaction.d.ts +25 -0
  55. package/browser/psbt/PsbtTransaction.d.ts.map +1 -0
  56. package/browser/psbt/bip371.d.ts.map +1 -1
  57. package/browser/psbt/types.d.ts +5 -71
  58. package/browser/psbt/types.d.ts.map +1 -1
  59. package/browser/psbt/validation.d.ts +1 -1
  60. package/browser/psbt/validation.d.ts.map +1 -1
  61. package/browser/psbt.d.ts +26 -40
  62. package/browser/psbt.d.ts.map +1 -1
  63. package/browser/script.d.ts.map +1 -1
  64. package/browser/transaction.d.ts +4 -4
  65. package/browser/transaction.d.ts.map +1 -1
  66. package/browser/types.d.ts +5 -3
  67. package/browser/types.d.ts.map +1 -1
  68. package/browser/workers/WorkerSigningPool.d.ts +24 -17
  69. package/browser/workers/WorkerSigningPool.d.ts.map +1 -1
  70. package/browser/workers/WorkerSigningPool.node.d.ts +19 -12
  71. package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -1
  72. package/browser/workers/WorkerSigningPool.sequential.d.ts +67 -0
  73. package/browser/workers/WorkerSigningPool.sequential.d.ts.map +1 -0
  74. package/browser/workers/WorkerSigningPool.worklet.d.ts +64 -0
  75. package/browser/workers/WorkerSigningPool.worklet.d.ts.map +1 -0
  76. package/browser/workers/index.browser.d.ts +16 -0
  77. package/browser/workers/index.browser.d.ts.map +1 -0
  78. package/browser/workers/index.d.ts +4 -64
  79. package/browser/workers/index.d.ts.map +1 -1
  80. package/browser/workers/index.js +28 -0
  81. package/browser/workers/index.node.d.ts +17 -0
  82. package/browser/workers/index.node.d.ts.map +1 -0
  83. package/browser/workers/index.react-native.d.ts +28 -0
  84. package/browser/workers/index.react-native.d.ts.map +1 -0
  85. package/browser/workers/index.shared.d.ts +15 -0
  86. package/browser/workers/index.shared.d.ts.map +1 -0
  87. package/browser/workers/psbt-parallel.d.ts +2 -3
  88. package/browser/workers/psbt-parallel.d.ts.map +1 -1
  89. package/browser/workers/types.d.ts +17 -0
  90. package/browser/workers/types.d.ts.map +1 -1
  91. package/build/address.d.ts +6 -2
  92. package/build/address.d.ts.map +1 -1
  93. package/build/address.js +32 -19
  94. package/build/address.js.map +1 -1
  95. package/build/bech32utils.js.map +1 -1
  96. package/build/block.d.ts.map +1 -1
  97. package/build/block.js +2 -4
  98. package/build/block.js.map +1 -1
  99. package/build/branded.d.ts +3 -14
  100. package/build/branded.d.ts.map +1 -1
  101. package/build/branded.js +0 -5
  102. package/build/branded.js.map +1 -1
  103. package/build/crypto.d.ts +1 -1
  104. package/build/ecc/context.d.ts +25 -24
  105. package/build/ecc/context.d.ts.map +1 -1
  106. package/build/ecc/context.js +29 -101
  107. package/build/ecc/context.js.map +1 -1
  108. package/build/ecc/index.d.ts +1 -1
  109. package/build/ecc/index.d.ts.map +1 -1
  110. package/build/ecc/types.d.ts +7 -126
  111. package/build/ecc/types.d.ts.map +1 -1
  112. package/build/ecc/types.js +4 -1
  113. package/build/ecc/types.js.map +1 -1
  114. package/build/env.d.ts +13 -0
  115. package/build/env.d.ts.map +1 -0
  116. package/build/env.js +198 -0
  117. package/build/env.js.map +1 -0
  118. package/build/index.d.ts +8 -7
  119. package/build/index.d.ts.map +1 -1
  120. package/build/index.js +9 -7
  121. package/build/index.js.map +1 -1
  122. package/build/io/BinaryReader.d.ts +15 -15
  123. package/build/io/BinaryReader.d.ts.map +1 -1
  124. package/build/io/BinaryReader.js +17 -17
  125. package/build/io/BinaryReader.js.map +1 -1
  126. package/build/io/BinaryWriter.d.ts +17 -17
  127. package/build/io/BinaryWriter.d.ts.map +1 -1
  128. package/build/io/BinaryWriter.js +39 -39
  129. package/build/io/BinaryWriter.js.map +1 -1
  130. package/build/io/hex.d.ts.map +1 -1
  131. package/build/io/hex.js +2 -1
  132. package/build/io/hex.js.map +1 -1
  133. package/build/io/index.d.ts +0 -1
  134. package/build/io/index.d.ts.map +1 -1
  135. package/build/io/index.js +0 -2
  136. package/build/io/index.js.map +1 -1
  137. package/build/opcodes.d.ts +11 -0
  138. package/build/opcodes.d.ts.map +1 -1
  139. package/build/opcodes.js +19 -4
  140. package/build/opcodes.js.map +1 -1
  141. package/build/payments/bip341.d.ts +1 -2
  142. package/build/payments/bip341.d.ts.map +1 -1
  143. package/build/payments/bip341.js +1 -2
  144. package/build/payments/bip341.js.map +1 -1
  145. package/build/payments/embed.d.ts +1 -1
  146. package/build/payments/embed.d.ts.map +1 -1
  147. package/build/payments/embed.js +14 -14
  148. package/build/payments/embed.js.map +1 -1
  149. package/build/payments/p2ms.d.ts.map +1 -1
  150. package/build/payments/p2ms.js +21 -21
  151. package/build/payments/p2ms.js.map +1 -1
  152. package/build/payments/p2op.d.ts +1 -1
  153. package/build/payments/p2op.d.ts.map +1 -1
  154. package/build/payments/p2op.js +18 -18
  155. package/build/payments/p2op.js.map +1 -1
  156. package/build/payments/p2pk.d.ts +1 -1
  157. package/build/payments/p2pk.d.ts.map +1 -1
  158. package/build/payments/p2pk.js +17 -17
  159. package/build/payments/p2pk.js.map +1 -1
  160. package/build/payments/p2pkh.d.ts +1 -1
  161. package/build/payments/p2pkh.d.ts.map +1 -1
  162. package/build/payments/p2pkh.js +20 -20
  163. package/build/payments/p2pkh.js.map +1 -1
  164. package/build/payments/p2sh.d.ts.map +1 -1
  165. package/build/payments/p2sh.js +22 -20
  166. package/build/payments/p2sh.js.map +1 -1
  167. package/build/payments/p2tr.d.ts +2 -2
  168. package/build/payments/p2tr.d.ts.map +1 -1
  169. package/build/payments/p2tr.js +25 -26
  170. package/build/payments/p2tr.js.map +1 -1
  171. package/build/payments/p2wpkh.d.ts +1 -1
  172. package/build/payments/p2wpkh.d.ts.map +1 -1
  173. package/build/payments/p2wpkh.js +20 -20
  174. package/build/payments/p2wpkh.js.map +1 -1
  175. package/build/payments/p2wsh.d.ts.map +1 -1
  176. package/build/payments/p2wsh.js +22 -22
  177. package/build/payments/p2wsh.js.map +1 -1
  178. package/build/payments/types.d.ts +1 -1
  179. package/build/payments/types.d.ts.map +1 -1
  180. package/build/psbt/PsbtCache.d.ts +54 -0
  181. package/build/psbt/PsbtCache.d.ts.map +1 -0
  182. package/build/psbt/PsbtCache.js +249 -0
  183. package/build/psbt/PsbtCache.js.map +1 -0
  184. package/build/psbt/PsbtFinalizer.d.ts +21 -0
  185. package/build/psbt/PsbtFinalizer.d.ts.map +1 -0
  186. package/build/psbt/PsbtFinalizer.js +157 -0
  187. package/build/psbt/PsbtFinalizer.js.map +1 -0
  188. package/build/psbt/PsbtSigner.d.ts +32 -0
  189. package/build/psbt/PsbtSigner.d.ts.map +1 -0
  190. package/build/psbt/PsbtSigner.js +192 -0
  191. package/build/psbt/PsbtSigner.js.map +1 -0
  192. package/build/psbt/PsbtTransaction.d.ts +25 -0
  193. package/build/psbt/PsbtTransaction.d.ts.map +1 -0
  194. package/build/psbt/PsbtTransaction.js +61 -0
  195. package/build/psbt/PsbtTransaction.js.map +1 -0
  196. package/build/psbt/bip371.d.ts.map +1 -1
  197. package/build/psbt/bip371.js +6 -2
  198. package/build/psbt/bip371.js.map +1 -1
  199. package/build/psbt/psbtutils.js +1 -1
  200. package/build/psbt/psbtutils.js.map +1 -1
  201. package/build/psbt/types.d.ts +5 -71
  202. package/build/psbt/types.d.ts.map +1 -1
  203. package/build/psbt/validation.d.ts +1 -1
  204. package/build/psbt/validation.d.ts.map +1 -1
  205. package/build/psbt/validation.js +1 -1
  206. package/build/psbt/validation.js.map +1 -1
  207. package/build/psbt.d.ts +26 -40
  208. package/build/psbt.d.ts.map +1 -1
  209. package/build/psbt.js +180 -808
  210. package/build/psbt.js.map +1 -1
  211. package/build/script.d.ts.map +1 -1
  212. package/build/script.js +4 -4
  213. package/build/script.js.map +1 -1
  214. package/build/transaction.d.ts +4 -4
  215. package/build/transaction.d.ts.map +1 -1
  216. package/build/transaction.js +6 -5
  217. package/build/transaction.js.map +1 -1
  218. package/build/tsconfig.build.tsbuildinfo +1 -1
  219. package/build/types.d.ts +5 -3
  220. package/build/types.d.ts.map +1 -1
  221. package/build/types.js +14 -25
  222. package/build/types.js.map +1 -1
  223. package/build/workers/WorkerSigningPool.d.ts +24 -17
  224. package/build/workers/WorkerSigningPool.d.ts.map +1 -1
  225. package/build/workers/WorkerSigningPool.js +36 -25
  226. package/build/workers/WorkerSigningPool.js.map +1 -1
  227. package/build/workers/WorkerSigningPool.node.d.ts +19 -12
  228. package/build/workers/WorkerSigningPool.node.d.ts.map +1 -1
  229. package/build/workers/WorkerSigningPool.node.js +60 -28
  230. package/build/workers/WorkerSigningPool.node.js.map +1 -1
  231. package/build/workers/WorkerSigningPool.sequential.d.ts +76 -0
  232. package/build/workers/WorkerSigningPool.sequential.d.ts.map +1 -0
  233. package/build/workers/WorkerSigningPool.sequential.js +160 -0
  234. package/build/workers/WorkerSigningPool.sequential.js.map +1 -0
  235. package/build/workers/WorkerSigningPool.worklet.d.ts +79 -0
  236. package/build/workers/WorkerSigningPool.worklet.d.ts.map +1 -0
  237. package/build/workers/WorkerSigningPool.worklet.js +390 -0
  238. package/build/workers/WorkerSigningPool.worklet.js.map +1 -0
  239. package/build/workers/index.browser.d.ts +24 -0
  240. package/build/workers/index.browser.d.ts.map +1 -0
  241. package/build/workers/index.browser.js +30 -0
  242. package/build/workers/index.browser.js.map +1 -0
  243. package/build/workers/index.d.ts +6 -18
  244. package/build/workers/index.d.ts.map +1 -1
  245. package/build/workers/index.js +12 -14
  246. package/build/workers/index.js.map +1 -1
  247. package/build/workers/index.node.d.ts +38 -0
  248. package/build/workers/index.node.d.ts.map +1 -0
  249. package/build/workers/index.node.js +45 -0
  250. package/build/workers/index.node.js.map +1 -0
  251. package/build/workers/index.react-native.d.ts +28 -0
  252. package/build/workers/index.react-native.d.ts.map +1 -0
  253. package/build/workers/index.react-native.js +67 -0
  254. package/build/workers/index.react-native.js.map +1 -0
  255. package/build/workers/index.shared.d.ts +15 -0
  256. package/build/workers/index.shared.d.ts.map +1 -0
  257. package/build/workers/index.shared.js +20 -0
  258. package/build/workers/index.shared.js.map +1 -0
  259. package/build/workers/psbt-parallel.d.ts +2 -3
  260. package/build/workers/psbt-parallel.d.ts.map +1 -1
  261. package/build/workers/psbt-parallel.js +4 -4
  262. package/build/workers/psbt-parallel.js.map +1 -1
  263. package/build/workers/types.d.ts +17 -0
  264. package/build/workers/types.d.ts.map +1 -1
  265. package/build/workers/types.js.map +1 -1
  266. package/package.json +48 -9
  267. package/src/address.ts +53 -21
  268. package/src/bech32utils.ts +3 -3
  269. package/src/block.ts +17 -10
  270. package/src/branded.ts +15 -13
  271. package/src/crypto.ts +1 -1
  272. package/src/ecc/context.ts +36 -136
  273. package/src/ecc/index.ts +2 -2
  274. package/src/ecc/types.ts +7 -145
  275. package/src/env.ts +239 -0
  276. package/src/index.ts +57 -22
  277. package/src/io/BinaryReader.ts +18 -18
  278. package/src/io/BinaryWriter.ts +43 -43
  279. package/src/io/hex.ts +2 -1
  280. package/src/io/index.ts +0 -3
  281. package/src/opcodes.ts +21 -4
  282. package/src/payments/bip341.ts +5 -7
  283. package/src/payments/embed.ts +19 -19
  284. package/src/payments/p2ms.ts +34 -27
  285. package/src/payments/p2op.ts +22 -22
  286. package/src/payments/p2pk.ts +22 -22
  287. package/src/payments/p2pkh.ts +28 -28
  288. package/src/payments/p2sh.ts +33 -30
  289. package/src/payments/p2tr.ts +40 -40
  290. package/src/payments/p2wpkh.ts +30 -30
  291. package/src/payments/p2wsh.ts +29 -29
  292. package/src/payments/types.ts +1 -1
  293. package/src/psbt/PsbtCache.ts +325 -0
  294. package/src/psbt/PsbtFinalizer.ts +213 -0
  295. package/src/psbt/PsbtSigner.ts +302 -0
  296. package/src/psbt/PsbtTransaction.ts +82 -0
  297. package/src/psbt/bip371.ts +7 -3
  298. package/src/psbt/psbtutils.ts +1 -1
  299. package/src/psbt/types.ts +5 -94
  300. package/src/psbt/validation.ts +5 -12
  301. package/src/psbt.ts +376 -1201
  302. package/src/script.ts +6 -9
  303. package/src/transaction.ts +19 -15
  304. package/src/types.ts +33 -45
  305. package/src/workers/WorkerSigningPool.node.ts +72 -36
  306. package/src/workers/WorkerSigningPool.sequential.ts +191 -0
  307. package/src/workers/WorkerSigningPool.ts +48 -39
  308. package/src/workers/WorkerSigningPool.worklet.ts +522 -0
  309. package/src/workers/index.browser.ts +34 -0
  310. package/src/workers/index.node.ts +50 -0
  311. package/src/workers/index.react-native.ts +110 -0
  312. package/src/workers/index.shared.ts +58 -0
  313. package/src/workers/index.ts +14 -65
  314. package/src/workers/psbt-parallel.ts +8 -13
  315. package/src/workers/types.ts +26 -1
  316. package/test/address.spec.ts +2 -2
  317. package/test/bitcoin.core.spec.ts +5 -2
  318. package/test/browser/payments.spec.ts +151 -0
  319. package/test/browser/psbt.spec.ts +1510 -0
  320. package/test/browser/script.spec.ts +223 -0
  321. package/test/browser/setup.ts +13 -0
  322. package/test/browser/workers-signing.spec.ts +537 -0
  323. package/test/crypto.spec.ts +2 -2
  324. package/test/env.spec.ts +418 -0
  325. package/test/fixtures/core/base58_encode_decode.json +12 -48
  326. package/test/fixtures/core/base58_keys_invalid.json +50 -150
  327. package/test/fixtures/core/sighash.json +1 -3
  328. package/test/fixtures/core/tx_valid.json +133 -501
  329. package/test/fixtures/embed.json +3 -11
  330. package/test/fixtures/p2ms.json +21 -91
  331. package/test/fixtures/p2pk.json +5 -24
  332. package/test/fixtures/p2pkh.json +7 -36
  333. package/test/fixtures/p2sh.json +8 -54
  334. package/test/fixtures/p2tr.json +2 -6
  335. package/test/fixtures/p2wpkh.json +7 -36
  336. package/test/fixtures/p2wsh.json +14 -59
  337. package/test/fixtures/psbt.json +2 -6
  338. package/test/fixtures/script.json +12 -48
  339. package/test/integration/addresses.spec.ts +11 -5
  340. package/test/integration/bip32.spec.ts +1 -1
  341. package/test/integration/cltv.spec.ts +10 -6
  342. package/test/integration/csv.spec.ts +10 -9
  343. package/test/integration/payments.spec.ts +8 -4
  344. package/test/integration/taproot.spec.ts +26 -6
  345. package/test/integration/transactions.spec.ts +22 -8
  346. package/test/payments.spec.ts +1 -1
  347. package/test/payments.utils.ts +1 -1
  348. package/test/psbt.spec.ts +250 -64
  349. package/test/script_signature.spec.ts +1 -1
  350. package/test/transaction.spec.ts +18 -5
  351. package/test/tsconfig.json +6 -20
  352. package/test/workers-pool.spec.ts +65 -23
  353. package/test/workers-sequential.spec.ts +669 -0
  354. package/test/workers-signing.spec.ts +7 -3
  355. package/test/workers-worklet.spec.ts +500 -0
  356. package/test/workers.spec.ts +6 -7
  357. package/typedoc.json +39 -0
  358. package/vite.config.browser.ts +31 -6
  359. package/vitest.config.browser.ts +68 -0
  360. package/browser/ecpair.d.ts +0 -99
  361. package/browser/io/MemoryPool.d.ts +0 -220
  362. package/browser/io/MemoryPool.d.ts.map +0 -1
  363. package/build/io/MemoryPool.d.ts +0 -220
  364. package/build/io/MemoryPool.d.ts.map +0 -1
  365. package/build/io/MemoryPool.js +0 -309
  366. package/build/io/MemoryPool.js.map +0 -1
  367. package/src/ecpair.d.ts +0 -99
  368. package/src/io/MemoryPool.ts +0 -343
  369. package/test/taproot-cache.spec.ts +0 -694
package/src/psbt.ts CHANGED
@@ -1,32 +1,18 @@
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
- import type { BIP32Interface } from '@btc-vision/bip32';
23
- import type { ECPairInterface } from 'ecpair';
24
- import { fromOutputScript, isUnknownSegwitVersion, toOutputScript } from './address.js';
13
+ import { fromOutputScript, toOutputScript } from './address.js';
25
14
  import { bitcoin as btcNetwork } from './networks.js';
26
15
  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
16
  import {
31
17
  checkTaprootInputFields,
32
18
  checkTaprootOutputFields,
@@ -35,38 +21,50 @@ import {
35
21
  tapScriptFinalizer,
36
22
  } from './psbt/bip371.js';
37
23
  import { toXOnly } from './pubkey.js';
24
+ import * as bscript from './script.js';
25
+ import { Transaction } from './transaction.js';
26
+ import type { Bytes32, MessageHash, PublicKey, SchnorrSignature, Script } from './types.js';
27
+
28
+ import type {
29
+ AllScriptType,
30
+ FinalScriptsFunc,
31
+ FinalTaprootScriptsFunc,
32
+ HDSigner,
33
+ HDSignerAsync,
34
+ PsbtBaseExtended,
35
+ PsbtInputExtended,
36
+ PsbtOpts,
37
+ PsbtOptsOptional,
38
+ PsbtOutputExtended,
39
+ PsbtOutputExtendedAddress,
40
+ PsbtTxInput,
41
+ PsbtTxOutput,
42
+ Signer,
43
+ SignerAsync,
44
+ TaprootHashCheckSigner,
45
+ ValidateSigFunction,
46
+ } from './psbt/types.js';
47
+ // Import composition classes
48
+ import { PsbtCache } from './psbt/PsbtCache.js';
49
+ import { PsbtSigner } from './psbt/PsbtSigner.js';
38
50
  import {
39
- isP2TR,
40
- isP2WPKH,
41
- pubkeyInScript,
42
- witnessStackToScriptWitness,
43
- } from './psbt/psbtutils.js';
51
+ getFinalScripts as _getFinalScripts,
52
+ prepareFinalScripts as _prepareFinalScripts,
53
+ PsbtFinalizer,
54
+ } from './psbt/PsbtFinalizer.js';
55
+ import { PsbtTransaction, transactionFromBuffer } from './psbt/PsbtTransaction.js';
44
56
  import {
45
57
  check32Bit,
46
58
  checkCache,
47
59
  checkInputsForPartialSig,
48
60
  checkPartialSigSighashes,
49
61
  checkScriptForPubkey,
50
- checkTxEmpty,
51
62
  checkTxForDupeIns,
52
63
  checkTxInputCache,
53
64
  isFinalized,
54
65
  } 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';
66
+ import { checkInvalidP2WSH, classifyScript, getMeaningfulScript, range } from './psbt/utils.js';
67
+ import { witnessStackToScriptWitness } from './psbt/psbtutils.js';
70
68
 
71
69
  // Re-export types from the types module
72
70
  export type {
@@ -80,15 +78,13 @@ export type {
80
78
  PsbtOpts,
81
79
  PsbtInputExtended,
82
80
  PsbtOutputExtended,
83
- PsbtOutputExtendedAddress,
84
81
  PsbtOutputExtendedScript,
85
82
  HDSigner,
86
83
  HDSignerAsync,
87
- SignerAlternative,
88
84
  Signer,
89
85
  SignerAsync,
90
86
  TaprootHashCheckSigner,
91
- PsbtCache,
87
+ PsbtCacheInterface,
92
88
  TxCacheNumberKey,
93
89
  ScriptType,
94
90
  AllScriptType,
@@ -97,50 +93,75 @@ export type {
97
93
  FinalTaprootScriptsFunc,
98
94
  } from './psbt/types.js';
99
95
 
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';
96
+ // Re-export for backwards compatibility
97
+ export { getFinalScripts, prepareFinalScripts };
98
+ export { PsbtCache } from './psbt/PsbtCache.js';
99
+ export { PsbtSigner } from './psbt/PsbtSigner.js';
100
+ export { PsbtFinalizer } from './psbt/PsbtFinalizer.js';
101
+ export { PsbtTransaction, transactionFromBuffer } from './psbt/PsbtTransaction.js';
126
102
 
127
103
  /**
128
104
  * These are the default arguments for a Psbt instance.
129
105
  */
130
106
  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
107
  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
108
+ maximumFeeRate: 5000,
142
109
  };
143
110
 
111
+ /** Helper to create a Transaction from a buffer */
112
+ function txFromBuffer(buf: Uint8Array): Transaction {
113
+ return Transaction.fromBuffer(buf);
114
+ }
115
+
116
+ // Standalone exports that delegate to PsbtFinalizer
117
+ function getFinalScripts(
118
+ inputIndex: number,
119
+ input: PsbtInput,
120
+ script: Script,
121
+ isSegwit: boolean,
122
+ isP2SH: boolean,
123
+ isP2WSH: boolean,
124
+ canRunChecks: boolean = true,
125
+ solution?: Uint8Array[],
126
+ ): {
127
+ finalScriptSig: Script | undefined;
128
+ finalScriptWitness: Uint8Array | undefined;
129
+ } {
130
+ return _getFinalScripts(
131
+ inputIndex,
132
+ input,
133
+ script,
134
+ isSegwit,
135
+ isP2SH,
136
+ isP2WSH,
137
+ canRunChecks,
138
+ solution,
139
+ );
140
+ }
141
+
142
+ function prepareFinalScripts(
143
+ script: Uint8Array,
144
+ scriptType: string,
145
+ partialSig: import('bip174').PartialSig[],
146
+ isSegwit: boolean,
147
+ isP2SH: boolean,
148
+ isP2WSH: boolean,
149
+ solution?: Uint8Array[],
150
+ ): {
151
+ finalScriptSig: Script | undefined;
152
+ finalScriptWitness: Uint8Array | undefined;
153
+ } {
154
+ return _prepareFinalScripts(
155
+ script,
156
+ scriptType,
157
+ partialSig,
158
+ isSegwit,
159
+ isP2SH,
160
+ isP2WSH,
161
+ solution,
162
+ );
163
+ }
164
+
144
165
  /**
145
166
  * Psbt class can parse and generate a PSBT binary based off of the BIP174.
146
167
  * There are 6 roles that this class fulfills. (Explained in BIP174)
@@ -178,64 +199,46 @@ const DEFAULT_OPTS: PsbtOpts = {
178
199
  * Transaction Extractor: This role will perform some checks before returning a
179
200
  * Transaction object. Such as fee rate not being larger than maximumFeeRate etc.
180
201
  */
181
- /**
182
- * Psbt class can parse and generate a PSBT binary based off of the BIP174.
183
- */
184
202
  export class Psbt {
185
203
  readonly #cache: PsbtCache;
204
+ #signer: PsbtSigner | undefined;
205
+ #finalizer: PsbtFinalizer | undefined;
186
206
  readonly #opts: PsbtOpts;
187
207
 
188
- constructor(
208
+ public constructor(
189
209
  opts: PsbtOptsOptional = {},
190
210
  public data: PsbtBaseExtended = new PsbtBase(new PsbtTransaction()),
191
211
  ) {
192
212
  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
- };
213
+ const tx = (this.data.globalMap.unsignedTx as PsbtTransaction).tx;
214
+ this.#cache = new PsbtCache(tx);
202
215
 
203
216
  if (opts.version === 3) {
204
217
  this.setVersionTRUC();
205
218
  } else if (this.data.inputs.length === 0) this.setVersion(2);
206
219
  }
207
220
 
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 {
221
+ public get inputCount(): number {
219
222
  return this.data.inputs.length;
220
223
  }
221
224
 
222
- get version(): number {
225
+ public get version(): number {
223
226
  return this.#cache.tx.version;
224
227
  }
225
228
 
226
- set version(version: number) {
229
+ public set version(version: number) {
227
230
  this.setVersion(version);
228
231
  }
229
232
 
230
- get locktime(): number {
233
+ public get locktime(): number {
231
234
  return this.#cache.tx.locktime;
232
235
  }
233
236
 
234
- set locktime(locktime: number) {
237
+ public set locktime(locktime: number) {
235
238
  this.setLocktime(locktime);
236
239
  }
237
240
 
238
- get txInputs(): PsbtTxInput[] {
241
+ public get txInputs(): PsbtTxInput[] {
239
242
  return this.#cache.tx.ins.map((input) => ({
240
243
  hash: clone(input.hash) as Bytes32,
241
244
  index: input.index,
@@ -243,12 +246,14 @@ export class Psbt {
243
246
  }));
244
247
  }
245
248
 
246
- get txOutputs(): PsbtTxOutput[] {
249
+ public get txOutputs(): PsbtTxOutput[] {
247
250
  return this.#cache.tx.outs.map((output) => {
248
- let address;
251
+ let address: string | undefined;
249
252
  try {
250
253
  address = fromOutputScript(output.script, this.#opts.network);
251
- } catch (_) {}
254
+ } catch (_) {
255
+ // Not all scripts can be converted to an address
256
+ }
252
257
  return {
253
258
  script: clone(output.script) as Script,
254
259
  value: output.value,
@@ -257,21 +262,36 @@ export class Psbt {
257
262
  });
258
263
  }
259
264
 
260
- static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
265
+ /** Lazily initialized signer - created on first access */
266
+ get #lazySigner(): PsbtSigner {
267
+ if (!this.#signer) {
268
+ this.#signer = new PsbtSigner(this.#cache, txFromBuffer);
269
+ }
270
+ return this.#signer;
271
+ }
272
+
273
+ /** Lazily initialized finalizer - created on first access */
274
+ get #lazyFinalizer(): PsbtFinalizer {
275
+ if (!this.#finalizer) {
276
+ this.#finalizer = new PsbtFinalizer(this.#cache, txFromBuffer);
277
+ }
278
+ return this.#finalizer;
279
+ }
280
+
281
+ public static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
261
282
  const buffer = fromBase64(data);
262
283
  return this.fromBuffer(buffer, opts);
263
284
  }
264
285
 
265
- static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
286
+ public static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
266
287
  const buffer = fromHex(data);
267
288
  return this.fromBuffer(buffer, opts);
268
289
  }
269
290
 
270
- static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
291
+ public static fromBuffer(buffer: Uint8Array, opts: PsbtOptsOptional = {}): Psbt {
271
292
  const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer);
272
293
  const psbt = new Psbt(opts, psbtBase);
273
294
  checkTxForDupeIns(psbt.#cache.tx, psbt.#cache);
274
- // Check if restored PSBT has any signatures (partial or finalized)
275
295
  psbt.#cache.hasSignatures = psbt.data.inputs.some(
276
296
  (input) =>
277
297
  input.partialSig?.length ||
@@ -283,62 +303,62 @@ export class Psbt {
283
303
  return psbt;
284
304
  }
285
305
 
286
- combine(...those: Psbt[]): this {
306
+ public combine(...those: Psbt[]): this {
287
307
  this.data.combine(...those.map((o) => o.data));
288
308
  return this;
289
309
  }
290
310
 
291
- clone(): Psbt {
292
- // TODO: more efficient cloning
293
- const clonedOpts = JSON.parse(JSON.stringify(this.#opts)) as PsbtOptsOptional;
311
+ public clone(): Psbt {
312
+ const clonedOpts = structuredClone(this.#opts) as PsbtOptsOptional;
294
313
  return Psbt.fromBuffer(new Uint8Array(this.data.toBuffer()), clonedOpts);
295
314
  }
296
315
 
297
- setMaximumFeeRate(satoshiPerByte: number): void {
298
- check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
316
+ public get maximumFeeRate(): number {
317
+ return this.#opts.maximumFeeRate;
318
+ }
319
+
320
+ public setMaximumFeeRate(satoshiPerByte: number): void {
321
+ check32Bit(satoshiPerByte);
299
322
  this.#opts.maximumFeeRate = satoshiPerByte;
300
323
  }
301
324
 
302
- setVersion(version: number): this {
325
+ public setVersion(version: number): this {
303
326
  check32Bit(version);
304
327
  checkInputsForPartialSig(this.data.inputs, 'setVersion', this.#cache.hasSignatures);
305
- const c = this.#cache;
306
- c.tx.version = version;
307
- c.extractedTx = undefined;
328
+ this.#cache.tx.version = version;
329
+ this.#cache.invalidate('outputs');
308
330
  return this;
309
331
  }
310
332
 
311
- setVersionTRUC(): this {
333
+ public setVersionTRUC(): this {
312
334
  return this.setVersion(Transaction.TRUC_VERSION);
313
335
  }
314
336
 
315
- setLocktime(locktime: number): this {
337
+ public setLocktime(locktime: number): this {
316
338
  check32Bit(locktime);
317
339
  checkInputsForPartialSig(this.data.inputs, 'setLocktime', this.#cache.hasSignatures);
318
- const c = this.#cache;
319
- c.tx.locktime = locktime;
320
- c.extractedTx = undefined;
340
+ this.#cache.tx.locktime = locktime;
341
+ this.#cache.invalidate('outputs');
321
342
  return this;
322
343
  }
323
344
 
324
- setInputSequence(inputIndex: number, sequence: number): this {
345
+ public setInputSequence(inputIndex: number, sequence: number): this {
325
346
  check32Bit(sequence);
326
347
  checkInputsForPartialSig(this.data.inputs, 'setInputSequence', this.#cache.hasSignatures);
327
- const c = this.#cache;
328
- if (c.tx.ins.length <= inputIndex) {
348
+ if (this.#cache.tx.ins.length <= inputIndex) {
329
349
  throw new Error('Input index too high');
330
350
  }
331
- c.tx.ins[inputIndex]!.sequence = sequence;
332
- c.extractedTx = undefined;
351
+ this.#cache.tx.ins[inputIndex]!.sequence = sequence;
352
+ this.#cache.invalidate('outputs');
333
353
  return this;
334
354
  }
335
355
 
336
- addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs: boolean = true): this {
356
+ public addInputs(inputDatas: PsbtInputExtended[], checkPartialSigs: boolean = true): this {
337
357
  inputDatas.forEach((inputData) => this.addInput(inputData, checkPartialSigs));
338
358
  return this;
339
359
  }
340
360
 
341
- addInput(inputData: PsbtInputExtended, checkPartialSigs: boolean = true): this {
361
+ public addInput(inputData: PsbtInputExtended, checkPartialSigs: boolean = true): this {
342
362
  if (!inputData || inputData.hash === undefined || inputData.index === undefined) {
343
363
  throw new Error(
344
364
  `Invalid arguments for Psbt.addInput. ` +
@@ -353,7 +373,6 @@ export class Psbt {
353
373
  }
354
374
 
355
375
  if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
356
- // Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
357
376
  const normalizedInputData = inputData.witnessUtxo
358
377
  ? {
359
378
  ...inputData,
@@ -366,50 +385,28 @@ export class Psbt {
366
385
  },
367
386
  }
368
387
  : inputData;
369
- const c = this.#cache;
370
388
  this.data.addInput(normalizedInputData);
371
- const txIn = c.tx.ins[c.tx.ins.length - 1]!;
372
- checkTxInputCache(c, txIn);
389
+ const txIn = this.#cache.tx.ins[this.#cache.tx.ins.length - 1]!;
390
+ checkTxInputCache(this.#cache, txIn);
373
391
 
374
392
  const inputIndex = this.data.inputs.length - 1;
375
393
  const input = this.data.inputs[inputIndex]!;
376
394
  if (input.nonWitnessUtxo) {
377
- addNonWitnessTxCache(this.#cache, input, inputIndex);
395
+ this.#cache.addNonWitnessTxCache(input, inputIndex, txFromBuffer);
378
396
  }
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;
397
+ this.#cache.invalidate('full');
386
398
  return this;
387
399
  }
388
400
 
389
- addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs: boolean = true): this {
401
+ public addOutputs(outputDatas: PsbtOutputExtended[], checkPartialSigs: boolean = true): this {
390
402
  outputDatas.forEach((outputData) => this.addOutput(outputData, checkPartialSigs));
391
403
  return this;
392
404
  }
393
405
 
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 {
406
+ public addOutput(outputData: PsbtOutputExtended, checkPartialSigs: boolean = true): this {
406
407
  const hasAddress = 'address' in outputData;
407
408
  const hasScript = 'script' in outputData;
408
- if (
409
- !outputData ||
410
- outputData.value === undefined ||
411
- (!hasAddress && !hasScript)
412
- ) {
409
+ if (!outputData || outputData.value === undefined || (!hasAddress && !hasScript)) {
413
410
  throw new Error(
414
411
  `Invalid arguments for Psbt.addOutput. ` +
415
412
  `Requires single object with at least [script or address] and [value]`,
@@ -426,52 +423,53 @@ export class Psbt {
426
423
  }
427
424
  checkTaprootOutputFields(outputData, outputData, 'addOutput');
428
425
 
429
- const c = this.#cache;
430
426
  this.data.addOutput(outputData);
431
- c.fee = undefined;
432
- c.feeRate = undefined;
433
- c.extractedTx = undefined;
434
- c.taprootHashCache = undefined;
427
+ this.#cache.invalidate('outputs');
435
428
  return this;
436
429
  }
437
430
 
438
- extractTransaction(disableFeeCheck?: boolean, disableOutputChecks?: boolean): Transaction {
431
+ public extractTransaction(
432
+ disableFeeCheck?: boolean,
433
+ disableOutputChecks?: boolean,
434
+ ): Transaction {
439
435
  if (disableOutputChecks) {
440
- (this.data as unknown as { inputs: PsbtInput[] }).inputs = this.data.inputs.filter((i) => !i.partialSig);
436
+ (this.data as unknown as { inputs: PsbtInput[] }).inputs = this.data.inputs.filter(
437
+ (i) => !i.partialSig,
438
+ );
441
439
  }
442
440
 
443
441
  if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
444
- const c = this.#cache;
445
442
  if (!disableFeeCheck) {
446
- checkFees(this, c, this.#opts);
443
+ this.#cache.computeFeeRate(this.data.inputs, disableOutputChecks, txFromBuffer);
444
+ this.#cache.checkFees(this.#opts);
447
445
  }
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',
446
+ if (this.#cache.extractedTx) return this.#cache.extractedTx;
447
+ const tx = this.#cache.tx.clone();
448
+ this.#cache.finalizeAndComputeAmounts(
458
449
  this.data.inputs,
459
- this.#cache,
450
+ tx,
451
+ true,
460
452
  disableOutputChecks,
453
+ txFromBuffer,
461
454
  );
455
+ return tx;
462
456
  }
463
457
 
464
- getFee(disableOutputChecks: boolean = false): number {
465
- return getTxCacheValue('fee', 'fee', this.data.inputs, this.#cache, disableOutputChecks);
458
+ public getFeeRate(disableOutputChecks: boolean = false): number {
459
+ return this.#cache.computeFeeRate(this.data.inputs, disableOutputChecks, txFromBuffer);
466
460
  }
467
461
 
468
- finalizeAllInputs(): this {
469
- checkForInput(this.data.inputs, 0); // making sure we have at least one
462
+ public getFee(disableOutputChecks: boolean = false): number {
463
+ return this.#cache.computeFee(this.data.inputs, disableOutputChecks, txFromBuffer);
464
+ }
465
+
466
+ public finalizeAllInputs(): this {
467
+ checkForInput(this.data.inputs, 0);
470
468
  range(this.data.inputs.length).forEach((idx) => this.finalizeInput(idx));
471
469
  return this;
472
470
  }
473
471
 
474
- finalizeInput(
472
+ public finalizeInput(
475
473
  inputIndex: number,
476
474
  finalScriptsFunc?: FinalScriptsFunc | FinalTaprootScriptsFunc,
477
475
  canRunChecks?: boolean,
@@ -493,7 +491,7 @@ export class Psbt {
493
491
  );
494
492
  }
495
493
 
496
- finalizeTaprootInput(
494
+ public finalizeTaprootInput(
497
495
  inputIndex: number,
498
496
  tapLeafHashToFinalize?: Bytes32,
499
497
  finalScriptsFunc: FinalTaprootScriptsFunc = tapScriptFinalizer,
@@ -509,54 +507,53 @@ export class Psbt {
509
507
  throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
510
508
  }
511
509
 
512
- getInputType(inputIndex: number): AllScriptType {
510
+ public getInputType(inputIndex: number): AllScriptType {
513
511
  const input = checkForInput(this.data.inputs, inputIndex);
514
- const script = getScriptFromUtxo(inputIndex, input, this.#cache);
512
+ const script = this.#cache.getScriptFromUtxo(inputIndex, input, txFromBuffer);
515
513
  const result = getMeaningfulScript(
516
514
  script,
517
515
  inputIndex,
518
516
  'input',
519
- input.redeemScript ||
520
- redeemFromFinalScriptSig(input.finalScriptSig),
517
+ input.redeemScript || this.#cache.redeemFromFinalScriptSig(input.finalScriptSig),
521
518
  input.witnessScript ||
522
- redeemFromFinalWitnessScript(input.finalScriptWitness),
519
+ this.#cache.redeemFromFinalWitnessScript(input.finalScriptWitness),
523
520
  );
524
521
  const type = result.type === 'raw' ? '' : result.type + '-';
525
522
  const mainType = classifyScript(result.meaningfulScript);
526
523
  return (type + mainType) as AllScriptType;
527
524
  }
528
525
 
529
- inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean {
526
+ public inputHasPubkey(inputIndex: number, pubkey: PublicKey): boolean {
530
527
  const input = checkForInput(this.data.inputs, inputIndex);
531
- return pubkeyInInput(pubkey, input, inputIndex, this.#cache);
528
+ return this.#cache.pubkeyInInput(pubkey, input, inputIndex, txFromBuffer);
532
529
  }
533
530
 
534
- inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
531
+ public inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
535
532
  const input = checkForInput(this.data.inputs, inputIndex);
536
- const derivationIsMine = bip32DerivationIsMine(root);
533
+ const derivationIsMine = this.#lazySigner.bip32DerivationIsMine(root);
537
534
  return !!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine);
538
535
  }
539
536
 
540
- outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean {
537
+ public outputHasPubkey(outputIndex: number, pubkey: PublicKey): boolean {
541
538
  const output = checkForOutput(this.data.outputs, outputIndex);
542
- return pubkeyInOutput(pubkey, output, outputIndex, this.#cache);
539
+ return this.#cache.pubkeyInOutput(pubkey, output, outputIndex);
543
540
  }
544
541
 
545
- outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
542
+ public outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
546
543
  const output = checkForOutput(this.data.outputs, outputIndex);
547
- const derivationIsMine = bip32DerivationIsMine(root);
544
+ const derivationIsMine = this.#lazySigner.bip32DerivationIsMine(root);
548
545
  return !!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine);
549
546
  }
550
547
 
551
- validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
552
- checkForInput(this.data.inputs, 0); // making sure we have at least one
548
+ public validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean {
549
+ checkForInput(this.data.inputs, 0);
553
550
  const results = range(this.data.inputs.length).map((idx) =>
554
551
  this.validateSignaturesOfInput(idx, validator),
555
552
  );
556
- return results.reduce((final, res) => res && final, true);
553
+ return results.every((res) => res);
557
554
  }
558
555
 
559
- validateSignaturesOfInput(
556
+ public validateSignaturesOfInput(
560
557
  inputIndex: number,
561
558
  validator: ValidateSigFunction,
562
559
  pubkey?: PublicKey,
@@ -568,7 +565,10 @@ export class Psbt {
568
565
  return this.#validateSignaturesOfInput(inputIndex, validator, pubkey);
569
566
  }
570
567
 
571
- signAllInputsHD(hdKeyPair: HDSigner, sighashTypes: number[] = [Transaction.SIGHASH_ALL]): this {
568
+ public signAllInputsHD(
569
+ hdKeyPair: HDSigner,
570
+ sighashTypes: number[] = [Transaction.SIGHASH_ALL],
571
+ ): this {
572
572
  if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
573
573
  throw new Error('Need HDSigner to sign input');
574
574
  }
@@ -588,39 +588,35 @@ export class Psbt {
588
588
  return this;
589
589
  }
590
590
 
591
- signAllInputsHDAsync(
591
+ public async signAllInputsHDAsync(
592
592
  hdKeyPair: HDSigner | HDSignerAsync,
593
593
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
594
594
  ): 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
- }
595
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
596
+ throw new Error('Need HDSigner to sign input');
597
+ }
599
598
 
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
- });
599
+ const results: boolean[] = [];
600
+ const promises: Array<Promise<void>> = [];
601
+ for (const i of range(this.data.inputs.length)) {
602
+ promises.push(
603
+ this.signInputHDAsync(i, hdKeyPair, sighashTypes).then(
604
+ () => {
605
+ results.push(true);
606
+ },
607
+ () => {
608
+ results.push(false);
609
+ },
610
+ ),
611
+ );
612
+ }
613
+ await Promise.all(promises);
614
+ if (results.every((v) => !v)) {
615
+ throw new Error('No inputs were signed');
616
+ }
621
617
  }
622
618
 
623
- signInputHD(
619
+ public signInputHD(
624
620
  inputIndex: number,
625
621
  hdKeyPair: HDSigner,
626
622
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
@@ -628,41 +624,32 @@ export class Psbt {
628
624
  if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
629
625
  throw new Error('Need HDSigner to sign input');
630
626
  }
631
- const signers = getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
627
+ const signers = this.#lazySigner.getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
632
628
  signers.forEach((signer) => this.signInput(inputIndex, signer, sighashTypes));
633
629
  return this;
634
630
  }
635
631
 
636
- signInputHDAsync(
632
+ public async signInputHDAsync(
637
633
  inputIndex: number,
638
634
  hdKeyPair: HDSigner | HDSignerAsync,
639
635
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
640
636
  ): 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
- });
637
+ if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
638
+ throw new Error('Need HDSigner to sign input');
639
+ }
640
+ const signers = this.#lazySigner.getSignersFromHD(inputIndex, this.data.inputs, hdKeyPair);
641
+ const promises = signers.map((signer) =>
642
+ this.signInputAsync(inputIndex, signer, sighashTypes),
643
+ );
644
+ await Promise.all(promises);
655
645
  }
656
646
 
657
- signAllInputs(
658
- keyPair: Signer | SignerAlternative | BIP32Interface | ECPairInterface,
647
+ public signAllInputs(
648
+ keyPair: Signer | HDSigner,
659
649
  sighashTypes?: number[],
660
650
  ): this {
661
651
  if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
662
652
 
663
- // TODO: Add a pubkey/pubkeyhash cache to each input
664
- // as input information is added, then eventually
665
- // optimize this method.
666
653
  const results: boolean[] = [];
667
654
  for (const i of range(this.data.inputs.length)) {
668
655
  try {
@@ -678,43 +665,35 @@ export class Psbt {
678
665
  return this;
679
666
  }
680
667
 
681
- signAllInputsAsync(
682
- keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | ECPairInterface,
668
+ public async signAllInputsAsync(
669
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
683
670
  sighashTypes?: number[],
684
671
  ): 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
- });
672
+ if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
673
+
674
+ const results: boolean[] = [];
675
+ const promises: Array<Promise<void>> = [];
676
+ for (const [i] of this.data.inputs.entries()) {
677
+ promises.push(
678
+ this.signInputAsync(i, keyPair, sighashTypes).then(
679
+ () => {
680
+ results.push(true);
681
+ },
682
+ () => {
683
+ results.push(false);
684
+ },
685
+ ),
686
+ );
687
+ }
688
+ await Promise.all(promises);
689
+ if (results.every((v) => !v)) {
690
+ throw new Error('No inputs were signed');
691
+ }
713
692
  }
714
693
 
715
- signInput(
694
+ public signInput(
716
695
  inputIndex: number,
717
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
696
+ keyPair: Signer | HDSigner,
718
697
  sighashTypes?: number[],
719
698
  ): this {
720
699
  if (!keyPair || !keyPair.publicKey) {
@@ -729,9 +708,9 @@ export class Psbt {
729
708
  return this.#signInput(inputIndex, keyPair, sighashTypes);
730
709
  }
731
710
 
732
- signTaprootInput(
711
+ public signTaprootInput(
733
712
  inputIndex: number,
734
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
713
+ keyPair: Signer | HDSigner,
735
714
  tapLeafHashToSign?: Uint8Array,
736
715
  sighashTypes?: number[],
737
716
  ): this {
@@ -753,75 +732,70 @@ export class Psbt {
753
732
  throw new Error(`Input #${inputIndex} is not of type Taproot.`);
754
733
  }
755
734
 
756
- signInputAsync(
735
+ public async signInputAsync(
757
736
  inputIndex: number,
758
- keyPair: Signer | SignerAlternative | SignerAsync | HDSigner | HDSignerAsync | BIP32Interface | ECPairInterface,
737
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
759
738
  sighashTypes?: number[],
760
739
  ): Promise<void> {
761
- return Promise.resolve().then(() => {
762
- if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
763
-
764
- const input = checkForInput(this.data.inputs, inputIndex);
765
- if (isTaprootInput(input))
766
- return this.#signTaprootInputAsync(
767
- inputIndex,
768
- input,
769
- keyPair,
770
- undefined,
771
- sighashTypes,
772
- );
773
-
774
- return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
775
- });
740
+ if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
741
+
742
+ const input = checkForInput(this.data.inputs, inputIndex);
743
+ if (isTaprootInput(input))
744
+ return this.#signTaprootInputAsync(
745
+ inputIndex,
746
+ input,
747
+ keyPair,
748
+ undefined,
749
+ sighashTypes,
750
+ );
751
+
752
+ return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
776
753
  }
777
754
 
778
- signTaprootInputAsync(
755
+ public async signTaprootInputAsync(
779
756
  inputIndex: number,
780
- keyPair: Signer | SignerAlternative | SignerAsync | HDSigner | HDSignerAsync | BIP32Interface | ECPairInterface,
757
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
781
758
  tapLeafHash?: Uint8Array,
782
759
  sighashTypes?: number[],
783
760
  ): Promise<void> {
784
- return Promise.resolve().then(() => {
785
- if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
786
-
787
- const input = checkForInput(this.data.inputs, inputIndex);
788
- if (isTaprootInput(input))
789
- return this.#signTaprootInputAsync(
790
- inputIndex,
791
- input,
792
- keyPair,
793
- tapLeafHash,
794
- sighashTypes,
795
- );
796
-
797
- throw new Error(`Input #${inputIndex} is not of type Taproot.`);
798
- });
761
+ if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
762
+
763
+ const input = checkForInput(this.data.inputs, inputIndex);
764
+ if (isTaprootInput(input))
765
+ return this.#signTaprootInputAsync(
766
+ inputIndex,
767
+ input,
768
+ keyPair,
769
+ tapLeafHash,
770
+ sighashTypes,
771
+ );
772
+
773
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
799
774
  }
800
775
 
801
- toBuffer(): Uint8Array {
776
+ public toBuffer(): Uint8Array {
802
777
  checkCache(this.#cache);
803
778
  return new Uint8Array(this.data.toBuffer());
804
779
  }
805
780
 
806
- toHex(): string {
781
+ public toHex(): string {
807
782
  checkCache(this.#cache);
808
783
  return this.data.toHex();
809
784
  }
810
785
 
811
- toBase64(): string {
786
+ public toBase64(): string {
812
787
  checkCache(this.#cache);
813
788
  return this.data.toBase64();
814
789
  }
815
790
 
816
- updateGlobal(updateData: PsbtGlobalUpdate): this {
791
+ public updateGlobal(updateData: PsbtGlobalUpdate): this {
817
792
  this.data.updateGlobal(updateData);
818
793
  return this;
819
794
  }
820
795
 
821
- updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
796
+ public updateInput(inputIndex: number, updateData: PsbtInputUpdate): this {
822
797
  if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
823
798
  checkTaprootInputFields(this.data.inputs[inputIndex]!, updateData, 'updateInput');
824
- // Convert witnessUtxo for bip174 v3 compatibility (value: bigint, script: Uint8Array)
825
799
  const normalizedUpdate = updateData.witnessUtxo
826
800
  ? {
827
801
  ...updateData,
@@ -836,12 +810,16 @@ export class Psbt {
836
810
  : updateData;
837
811
  this.data.updateInput(inputIndex, normalizedUpdate);
838
812
  if (updateData.nonWitnessUtxo) {
839
- addNonWitnessTxCache(this.#cache, this.data.inputs[inputIndex]!, inputIndex);
813
+ this.#cache.addNonWitnessTxCache(
814
+ this.data.inputs[inputIndex]!,
815
+ inputIndex,
816
+ txFromBuffer,
817
+ );
840
818
  }
841
819
  return this;
842
820
  }
843
821
 
844
- updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
822
+ public updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this {
845
823
  const outputData = this.data.outputs[outputIndex]!;
846
824
  checkTaprootOutputFields(outputData, updateData, 'updateOutput');
847
825
 
@@ -849,54 +827,52 @@ export class Psbt {
849
827
  return this;
850
828
  }
851
829
 
852
- addUnknownKeyValToGlobal(keyVal: KeyValue): this {
830
+ public addUnknownKeyValToGlobal(keyVal: KeyValue): this {
853
831
  this.data.addUnknownKeyValToGlobal(keyVal);
854
832
  return this;
855
833
  }
856
834
 
857
- addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
835
+ public addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
858
836
  this.data.addUnknownKeyValToInput(inputIndex, keyVal);
859
837
  return this;
860
838
  }
861
839
 
862
- addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
840
+ public addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
863
841
  this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
864
842
  return this;
865
843
  }
866
844
 
867
- clearFinalizedInput(inputIndex: number): this {
845
+ public clearFinalizedInput(inputIndex: number): this {
868
846
  this.data.clearFinalizedInput(inputIndex);
869
847
  return this;
870
848
  }
871
849
 
872
- checkTaprootHashesForSig(
850
+ public checkTaprootHashesForSig(
873
851
  inputIndex: number,
874
852
  input: PsbtInput,
875
- keyPair: Signer | SignerAlternative | SignerAsync | HDSigner | HDSignerAsync | TaprootHashCheckSigner | BIP32Interface | ECPairInterface,
853
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync | TaprootHashCheckSigner,
876
854
  tapLeafHashToSign?: Uint8Array,
877
855
  allowedSighashTypes?: number[],
878
- ): { hash: Bytes32; leafHash?: Bytes32 }[] {
856
+ ): { hash: MessageHash; leafHash?: Bytes32 }[] {
879
857
  if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
880
858
  throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
881
859
 
882
- const pubkey = keyPair.publicKey instanceof Uint8Array
883
- ? keyPair.publicKey
884
- : new Uint8Array(keyPair.publicKey);
860
+ const pubkey =
861
+ keyPair.publicKey instanceof Uint8Array
862
+ ? keyPair.publicKey
863
+ : new Uint8Array(keyPair.publicKey);
885
864
 
886
- const hashesForSig = getTaprootHashesForSig(
865
+ const hashesForSig = this.#lazySigner.getTaprootHashesForSig(
887
866
  inputIndex,
888
867
  input,
889
868
  this.data.inputs,
890
869
  pubkey,
891
- this.#cache,
892
870
  tapLeafHashToSign,
893
871
  allowedSighashTypes,
894
872
  );
895
873
 
896
874
  if (!hashesForSig || !hashesForSig.length)
897
- throw new Error(
898
- `Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`,
899
- );
875
+ throw new Error(`Can not sign for input #${inputIndex} with the key ${toHex(pubkey)}`);
900
876
 
901
877
  return hashesForSig;
902
878
  }
@@ -907,10 +883,9 @@ export class Psbt {
907
883
  finalScriptsFunc: FinalScriptsFunc = getFinalScripts,
908
884
  canRunChecks: boolean = true,
909
885
  ): this {
910
- const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
886
+ const { script, isP2SH, isP2WSH, isSegwit } = this.#lazyFinalizer.getScriptFromInput(
911
887
  inputIndex,
912
888
  input,
913
- this.#cache,
914
889
  );
915
890
  if (!script) throw new Error(`No script found for input #${inputIndex}`);
916
891
 
@@ -944,7 +919,6 @@ export class Psbt {
944
919
  if (!input.witnessUtxo)
945
920
  throw new Error(`Cannot finalize input #${inputIndex}. Missing witness utxo.`);
946
921
 
947
- // Check key spend first. Increased privacy and reduced block space.
948
922
  if (input.tapKeySig) {
949
923
  const payment = payments.p2tr({
950
924
  output: input.witnessUtxo.script as Script,
@@ -973,17 +947,15 @@ export class Psbt {
973
947
  pubkey?: PublicKey,
974
948
  ): boolean {
975
949
  const input = this.data.inputs[inputIndex];
976
- const partialSig = (input || {}).partialSig;
950
+ const partialSig = input?.partialSig;
977
951
  if (!input || !partialSig || partialSig.length < 1)
978
952
  throw new Error('No signatures to validate');
979
953
  if (typeof validator !== 'function')
980
954
  throw new Error('Need validator function to validate signatures');
981
- const mySigs = pubkey
982
- ? partialSig.filter((sig) => equals(sig.pubkey, pubkey))
983
- : partialSig;
955
+ const mySigs = pubkey ? partialSig.filter((sig) => equals(sig.pubkey, pubkey)) : partialSig;
984
956
  if (mySigs.length < 1) throw new Error('No signatures for this pubkey');
985
957
  const results: boolean[] = [];
986
- let hashCache: Bytes32 | undefined;
958
+ let hashCache: MessageHash | undefined;
987
959
  let scriptCache: Script | undefined;
988
960
  let sighashCache: number | undefined;
989
961
  for (const pSig of mySigs) {
@@ -992,12 +964,11 @@ export class Psbt {
992
964
  const sig = bscript.signature.decode(pSigSignature);
993
965
  const { hash, script } =
994
966
  sighashCache !== sig.hashType || !hashCache || !scriptCache
995
- ? getHashForSig(
967
+ ? this.#lazySigner.getHashForSig(
996
968
  inputIndex,
997
969
  Object.assign({}, input, {
998
970
  sighashType: sig.hashType,
999
971
  }),
1000
- this.#cache,
1001
972
  true,
1002
973
  )
1003
974
  : { hash: hashCache, script: scriptCache };
@@ -1016,8 +987,8 @@ export class Psbt {
1016
987
  pubkey?: PublicKey,
1017
988
  ): boolean {
1018
989
  const input = this.data.inputs[inputIndex]!;
1019
- const tapKeySig = (input || {}).tapKeySig;
1020
- const tapScriptSig = (input || {}).tapScriptSig;
990
+ const tapKeySig = input?.tapKeySig;
991
+ const tapScriptSig = input?.tapScriptSig;
1021
992
  if (!input && !tapKeySig && !(tapScriptSig && !tapScriptSig.length))
1022
993
  throw new Error('No signatures to validate');
1023
994
  if (typeof validator !== 'function')
@@ -1025,8 +996,8 @@ export class Psbt {
1025
996
 
1026
997
  const xPubkey = pubkey ? toXOnly(pubkey) : undefined;
1027
998
  const allHashses = xPubkey
1028
- ? getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey, this.#cache)
1029
- : getAllTaprootHashesForSig(inputIndex, input, this.data.inputs, this.#cache);
999
+ ? this.#lazySigner.getTaprootHashesForSig(inputIndex, input, this.data.inputs, xPubkey)
1000
+ : this.#lazySigner.getAllTaprootHashesForSig(inputIndex, input, this.data.inputs);
1030
1001
 
1031
1002
  if (!allHashses.length) throw new Error('No signatures for this pubkey');
1032
1003
 
@@ -1036,7 +1007,7 @@ export class Psbt {
1036
1007
  const isValidTapkeySig = validator(
1037
1008
  tapKeyHash.pubkey,
1038
1009
  tapKeyHash.hash,
1039
- trimTaprootSig(tapKeySig),
1010
+ this.#lazySigner.trimTaprootSig(tapKeySig),
1040
1011
  );
1041
1012
  if (!isValidTapkeySig) return false;
1042
1013
  validationResultCount++;
@@ -1050,7 +1021,7 @@ export class Psbt {
1050
1021
  const isValidTapScriptSig = validator(
1051
1022
  tapSigPubkey,
1052
1023
  tapSigHash.hash,
1053
- trimTaprootSig(tapSig.signature),
1024
+ this.#lazySigner.trimTaprootSig(tapSig.signature),
1054
1025
  );
1055
1026
  if (!isValidTapScriptSig) return false;
1056
1027
  validationResultCount++;
@@ -1063,22 +1034,22 @@ export class Psbt {
1063
1034
 
1064
1035
  #signInput(
1065
1036
  inputIndex: number,
1066
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
1037
+ keyPair: Signer | HDSigner,
1067
1038
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
1068
1039
  ): this {
1069
- const pubkey = keyPair.publicKey instanceof Uint8Array
1070
- ? keyPair.publicKey
1071
- : new Uint8Array(keyPair.publicKey);
1040
+ const pubkey =
1041
+ keyPair.publicKey instanceof Uint8Array
1042
+ ? keyPair.publicKey
1043
+ : new Uint8Array(keyPair.publicKey);
1072
1044
 
1073
- const { hash, sighashType } = getHashAndSighashType(
1045
+ const { hash, sighashType } = this.#lazySigner.getHashAndSighashType(
1074
1046
  this.data.inputs,
1075
1047
  inputIndex,
1076
1048
  pubkey,
1077
- this.#cache,
1078
1049
  sighashTypes,
1079
1050
  );
1080
1051
 
1081
- const sig = keyPair.sign(hash) as Uint8Array;
1052
+ const sig = keyPair.sign(hash);
1082
1053
  const partialSig = [
1083
1054
  {
1084
1055
  pubkey,
@@ -1097,7 +1068,7 @@ export class Psbt {
1097
1068
  #signTaprootInput(
1098
1069
  inputIndex: number,
1099
1070
  input: PsbtInput,
1100
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | ECPairInterface,
1071
+ keyPair: Signer | HDSigner,
1101
1072
  tapLeafHashToSign?: Uint8Array,
1102
1073
  allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
1103
1074
  ): this {
@@ -1110,7 +1081,6 @@ export class Psbt {
1110
1081
  if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
1111
1082
  throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
1112
1083
 
1113
- // checkTaprootHashesForSig validates signSchnorr exists
1114
1084
  const hashesForSig = this.checkTaprootHashesForSig(
1115
1085
  inputIndex,
1116
1086
  input,
@@ -1118,7 +1088,7 @@ export class Psbt {
1118
1088
  tapLeafHashToSign,
1119
1089
  allowedSighashTypes,
1120
1090
  );
1121
- const signSchnorr = (keyPair.signSchnorr as (h: Uint8Array) => Uint8Array).bind(keyPair);
1091
+ const signSchnorr = (keyPair.signSchnorr as (h: MessageHash) => SchnorrSignature).bind(keyPair);
1122
1092
 
1123
1093
  const tapKeySig = hashesForSig
1124
1094
  .filter((h) => !h.leafHash)
@@ -1153,41 +1123,40 @@ export class Psbt {
1153
1123
  return this;
1154
1124
  }
1155
1125
 
1156
- #signInputAsync(
1126
+ async #signInputAsync(
1157
1127
  inputIndex: number,
1158
- keyPair: Signer | SignerAlternative | SignerAsync | HDSigner | HDSignerAsync | BIP32Interface | ECPairInterface,
1128
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
1159
1129
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
1160
1130
  ): Promise<void> {
1161
- const pubkey = keyPair.publicKey instanceof Uint8Array
1162
- ? keyPair.publicKey
1163
- : new Uint8Array(keyPair.publicKey);
1131
+ const pubkey =
1132
+ keyPair.publicKey instanceof Uint8Array
1133
+ ? keyPair.publicKey
1134
+ : new Uint8Array(keyPair.publicKey);
1164
1135
 
1165
- const { hash, sighashType } = getHashAndSighashType(
1136
+ const { hash, sighashType } = this.#lazySigner.getHashAndSighashType(
1166
1137
  this.data.inputs,
1167
1138
  inputIndex,
1168
1139
  pubkey,
1169
- this.#cache,
1170
1140
  sighashTypes,
1171
1141
  );
1172
1142
 
1173
- return Promise.resolve(keyPair.sign(hash)).then((signature) => {
1174
- const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
1175
- const partialSig = [
1176
- {
1177
- pubkey,
1178
- signature: bscript.signature.encode(sig, sighashType),
1179
- },
1180
- ];
1143
+ const signature = await keyPair.sign(hash);
1144
+ const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
1145
+ const partialSig = [
1146
+ {
1147
+ pubkey,
1148
+ signature: bscript.signature.encode(sig, sighashType),
1149
+ },
1150
+ ];
1181
1151
 
1182
- this.data.updateInput(inputIndex, { partialSig });
1183
- this.#cache.hasSignatures = true;
1184
- });
1152
+ this.data.updateInput(inputIndex, { partialSig });
1153
+ this.#cache.hasSignatures = true;
1185
1154
  }
1186
1155
 
1187
1156
  async #signTaprootInputAsync(
1188
1157
  inputIndex: number,
1189
1158
  input: PsbtInput,
1190
- keyPair: Signer | SignerAlternative | SignerAsync | HDSigner | HDSignerAsync | BIP32Interface | ECPairInterface,
1159
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
1191
1160
  tapLeafHash?: Uint8Array,
1192
1161
  sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
1193
1162
  ): Promise<void> {
@@ -1200,7 +1169,6 @@ export class Psbt {
1200
1169
  if (!('signSchnorr' in keyPair) || typeof keyPair.signSchnorr !== 'function')
1201
1170
  throw new Error(`Need Schnorr Signer to sign taproot input #${inputIndex}.`);
1202
1171
 
1203
- // checkTaprootHashesForSig validates signSchnorr exists
1204
1172
  const hashesForSig = this.checkTaprootHashesForSig(
1205
1173
  inputIndex,
1206
1174
  input,
@@ -1209,826 +1177,33 @@ export class Psbt {
1209
1177
  sighashTypes,
1210
1178
  );
1211
1179
  const signSchnorr = (
1212
- keyPair.signSchnorr as (hash: Uint8Array) => Uint8Array | Promise<Uint8Array>
1180
+ keyPair.signSchnorr as (hash: MessageHash) => SchnorrSignature | Promise<SchnorrSignature>
1213
1181
  ).bind(keyPair);
1214
1182
 
1215
- type TapSignatureResult = { tapKeySig: Uint8Array } | { tapScriptSig: TapScriptSig[] };
1216
- const signaturePromises: Promise<TapSignatureResult>[] = [];
1217
-
1218
- const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
1183
+ const tapKeyHash = hashesForSig.find((h) => !h.leafHash);
1219
1184
  if (tapKeyHash) {
1220
- const tapKeySigPromise = Promise.resolve(signSchnorr(tapKeyHash.hash)).then((sig) => {
1221
- return {
1222
- tapKeySig: serializeTaprootSignature(sig, input.sighashType),
1223
- };
1224
- });
1225
- signaturePromises.push(tapKeySigPromise);
1185
+ const sig = await signSchnorr(tapKeyHash.hash);
1186
+ const tapKeySig = serializeTaprootSignature(sig, input.sighashType);
1187
+ this.data.updateInput(inputIndex, { tapKeySig });
1188
+ this.#cache.hasSignatures = true;
1226
1189
  }
1227
1190
 
1228
1191
  const tapScriptHashes = hashesForSig.filter(
1229
1192
  (h): h is typeof h & { leafHash: Bytes32 } => !!h.leafHash,
1230
1193
  );
1231
1194
  if (tapScriptHashes.length) {
1232
- const tapScriptSigPromises = tapScriptHashes.map(async (tsh) => {
1233
- const signature = await signSchnorr(tsh.hash);
1234
-
1235
- const tapScriptSig: TapScriptSig[] = [
1236
- {
1195
+ const tapScriptSigs = await Promise.all(
1196
+ tapScriptHashes.map(async (tsh) => {
1197
+ const signature = await signSchnorr(tsh.hash);
1198
+ return {
1237
1199
  pubkey: toXOnly(pubkey),
1238
1200
  signature: serializeTaprootSignature(signature, input.sighashType),
1239
1201
  leafHash: tsh.leafHash,
1240
- },
1241
- ];
1242
-
1243
- return { tapScriptSig };
1244
- });
1245
- signaturePromises.push(...tapScriptSigPromises);
1246
- }
1247
-
1248
- const results = await Promise.all(signaturePromises);
1249
- for (const v of results) {
1250
- this.data.updateInput(inputIndex, v as PsbtInputUpdate);
1251
- this.#cache.hasSignatures = true;
1252
- }
1253
- }
1254
- }
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}`,
1202
+ } as TapScriptSig;
1203
+ }),
1936
1204
  );
1205
+ this.data.updateInput(inputIndex, { tapScriptSig: tapScriptSigs });
1206
+ this.#cache.hasSignatures = true;
1937
1207
  }
1938
1208
  }
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
1209
  }