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