@btc-vision/bitcoin 6.5.6 → 7.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/HOW_TO_WRITE_GOOD_CODE.md +2436 -0
  2. package/benchmark/psbt-2000-inputs.bench.ts +178 -0
  3. package/benchmark/signing.bench.ts +147 -0
  4. package/browser/address.d.ts +57 -10
  5. package/browser/address.d.ts.map +1 -0
  6. package/browser/bech32utils.d.ts +9 -1
  7. package/browser/bech32utils.d.ts.map +1 -0
  8. package/browser/bip66.d.ts +11 -6
  9. package/browser/bip66.d.ts.map +1 -0
  10. package/browser/block.d.ts +117 -11
  11. package/browser/block.d.ts.map +1 -0
  12. package/browser/branded.d.ts +20 -0
  13. package/browser/branded.d.ts.map +1 -0
  14. package/browser/crypto/crypto.d.ts +1 -0
  15. package/browser/crypto/crypto.d.ts.map +1 -0
  16. package/browser/crypto.d.ts +46 -7
  17. package/browser/crypto.d.ts.map +1 -0
  18. package/browser/ecc/context.d.ts +129 -0
  19. package/browser/ecc/context.d.ts.map +1 -0
  20. package/browser/ecc/index.d.ts +11 -0
  21. package/browser/ecc/index.d.ts.map +1 -0
  22. package/browser/ecc/types.d.ts +128 -0
  23. package/browser/ecc/types.d.ts.map +1 -0
  24. package/browser/ecpair.d.ts +99 -0
  25. package/browser/errors.d.ts +124 -0
  26. package/browser/errors.d.ts.map +1 -0
  27. package/browser/index.d.ts +32 -5
  28. package/browser/index.d.ts.map +1 -0
  29. package/browser/index.js +12477 -101
  30. package/browser/io/BinaryReader.d.ts +276 -0
  31. package/browser/io/BinaryReader.d.ts.map +1 -0
  32. package/browser/io/BinaryWriter.d.ts +391 -0
  33. package/browser/io/BinaryWriter.d.ts.map +1 -0
  34. package/browser/io/MemoryPool.d.ts +220 -0
  35. package/browser/io/MemoryPool.d.ts.map +1 -0
  36. package/browser/io/base64.d.ts +13 -0
  37. package/browser/io/base64.d.ts.map +1 -0
  38. package/browser/io/hex.d.ts +67 -0
  39. package/browser/io/hex.d.ts.map +1 -0
  40. package/browser/io/index.d.ts +17 -0
  41. package/browser/io/index.d.ts.map +1 -0
  42. package/browser/io/utils.d.ts +199 -0
  43. package/browser/io/utils.d.ts.map +1 -0
  44. package/browser/merkle.d.ts +10 -1
  45. package/browser/merkle.d.ts.map +1 -0
  46. package/browser/networks.d.ts +70 -9
  47. package/browser/networks.d.ts.map +1 -0
  48. package/browser/opcodes.d.ts +1 -0
  49. package/browser/opcodes.d.ts.map +1 -0
  50. package/browser/payments/bip341.d.ts +35 -9
  51. package/browser/payments/bip341.d.ts.map +1 -0
  52. package/browser/payments/embed.d.ts +112 -1
  53. package/browser/payments/embed.d.ts.map +1 -0
  54. package/browser/payments/index.d.ts +17 -10
  55. package/browser/payments/index.d.ts.map +1 -0
  56. package/browser/payments/p2ms.d.ts +150 -0
  57. package/browser/payments/p2ms.d.ts.map +1 -0
  58. package/browser/payments/p2op.d.ts +150 -24
  59. package/browser/payments/p2op.d.ts.map +1 -0
  60. package/browser/payments/p2pk.d.ts +154 -1
  61. package/browser/payments/p2pk.d.ts.map +1 -0
  62. package/browser/payments/p2pkh.d.ts +176 -1
  63. package/browser/payments/p2pkh.d.ts.map +1 -0
  64. package/browser/payments/p2sh.d.ts +150 -1
  65. package/browser/payments/p2sh.d.ts.map +1 -0
  66. package/browser/payments/p2tr.d.ts +185 -1
  67. package/browser/payments/p2tr.d.ts.map +1 -0
  68. package/browser/payments/p2wpkh.d.ts +161 -1
  69. package/browser/payments/p2wpkh.d.ts.map +1 -0
  70. package/browser/payments/p2wsh.d.ts +146 -1
  71. package/browser/payments/p2wsh.d.ts.map +1 -0
  72. package/browser/payments/types.d.ts +94 -64
  73. package/browser/payments/types.d.ts.map +1 -0
  74. package/browser/psbt/bip371.d.ts +34 -8
  75. package/browser/psbt/bip371.d.ts.map +1 -0
  76. package/browser/psbt/psbtutils.d.ts +56 -16
  77. package/browser/psbt/psbtutils.d.ts.map +1 -0
  78. package/browser/psbt/types.d.ts +245 -0
  79. package/browser/psbt/types.d.ts.map +1 -0
  80. package/browser/psbt/utils.d.ts +64 -0
  81. package/browser/psbt/utils.d.ts.map +1 -0
  82. package/browser/psbt/validation.d.ts +84 -0
  83. package/browser/psbt/validation.d.ts.map +1 -0
  84. package/browser/psbt.d.ts +82 -118
  85. package/browser/psbt.d.ts.map +1 -0
  86. package/browser/pubkey.d.ts +27 -6
  87. package/browser/pubkey.d.ts.map +1 -0
  88. package/browser/push_data.d.ts +24 -2
  89. package/browser/push_data.d.ts.map +1 -0
  90. package/browser/script.d.ts +33 -8
  91. package/browser/script.d.ts.map +1 -0
  92. package/browser/script_number.d.ts +17 -0
  93. package/browser/script_number.d.ts.map +1 -0
  94. package/browser/script_signature.d.ts +23 -5
  95. package/browser/script_signature.d.ts.map +1 -0
  96. package/browser/transaction.d.ts +160 -18
  97. package/browser/transaction.d.ts.map +1 -0
  98. package/browser/types.d.ts +36 -38
  99. package/browser/types.d.ts.map +1 -0
  100. package/browser/workers/WorkerSigningPool.d.ts +143 -0
  101. package/browser/workers/WorkerSigningPool.d.ts.map +1 -0
  102. package/browser/workers/WorkerSigningPool.node.d.ts +116 -0
  103. package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -0
  104. package/browser/workers/ecc-bundle.d.ts +25 -0
  105. package/browser/workers/ecc-bundle.d.ts.map +1 -0
  106. package/browser/workers/index.d.ts +91 -0
  107. package/browser/workers/index.d.ts.map +1 -0
  108. package/browser/workers/psbt-parallel.d.ts +88 -0
  109. package/browser/workers/psbt-parallel.d.ts.map +1 -0
  110. package/browser/workers/signing-worker.d.ts +37 -0
  111. package/browser/workers/signing-worker.d.ts.map +1 -0
  112. package/browser/workers/types.d.ts +365 -0
  113. package/browser/workers/types.d.ts.map +1 -0
  114. package/build/address.d.ts +58 -11
  115. package/build/address.d.ts.map +1 -0
  116. package/build/address.js +82 -25
  117. package/build/address.js.map +1 -0
  118. package/build/bech32utils.d.ts +9 -1
  119. package/build/bech32utils.d.ts.map +1 -0
  120. package/build/bech32utils.js +10 -2
  121. package/build/bech32utils.js.map +1 -0
  122. package/build/bip66.d.ts +11 -6
  123. package/build/bip66.d.ts.map +1 -0
  124. package/build/bip66.js +32 -3
  125. package/build/bip66.js.map +1 -0
  126. package/build/block.d.ts +117 -11
  127. package/build/block.d.ts.map +1 -0
  128. package/build/block.js +202 -72
  129. package/build/block.js.map +1 -0
  130. package/build/branded.d.ts +20 -0
  131. package/build/branded.d.ts.map +1 -0
  132. package/build/branded.js +7 -0
  133. package/build/branded.js.map +1 -0
  134. package/build/crypto/crypto.d.ts +1 -0
  135. package/build/crypto/crypto.d.ts.map +1 -0
  136. package/build/crypto/crypto.js +1 -0
  137. package/build/crypto/crypto.js.map +1 -0
  138. package/build/crypto.d.ts +46 -7
  139. package/build/crypto.d.ts.map +1 -0
  140. package/build/crypto.js +65 -20
  141. package/build/crypto.js.map +1 -0
  142. package/build/ecc/context.d.ts +135 -0
  143. package/build/ecc/context.d.ts.map +1 -0
  144. package/build/ecc/context.js +232 -0
  145. package/build/ecc/context.js.map +1 -0
  146. package/build/ecc/index.d.ts +11 -0
  147. package/build/ecc/index.d.ts.map +1 -0
  148. package/build/ecc/index.js +11 -0
  149. package/build/ecc/index.js.map +1 -0
  150. package/build/ecc/types.d.ts +134 -0
  151. package/build/ecc/types.d.ts.map +1 -0
  152. package/build/ecc/types.js +8 -0
  153. package/build/ecc/types.js.map +1 -0
  154. package/build/errors.d.ts +124 -0
  155. package/build/errors.d.ts.map +1 -0
  156. package/build/errors.js +155 -0
  157. package/build/errors.js.map +1 -0
  158. package/build/index.d.ts +32 -5
  159. package/build/index.d.ts.map +1 -0
  160. package/build/index.js +26 -3
  161. package/build/index.js.map +1 -0
  162. package/build/io/BinaryReader.d.ts +276 -0
  163. package/build/io/BinaryReader.d.ts.map +1 -0
  164. package/build/io/BinaryReader.js +425 -0
  165. package/build/io/BinaryReader.js.map +1 -0
  166. package/build/io/BinaryWriter.d.ts +391 -0
  167. package/build/io/BinaryWriter.d.ts.map +1 -0
  168. package/build/io/BinaryWriter.js +611 -0
  169. package/build/io/BinaryWriter.js.map +1 -0
  170. package/build/io/MemoryPool.d.ts +220 -0
  171. package/build/io/MemoryPool.d.ts.map +1 -0
  172. package/build/io/MemoryPool.js +309 -0
  173. package/build/io/MemoryPool.js.map +1 -0
  174. package/build/io/base64.d.ts +13 -0
  175. package/build/io/base64.d.ts.map +1 -0
  176. package/build/io/base64.js +20 -0
  177. package/build/io/base64.js.map +1 -0
  178. package/build/io/hex.d.ts +67 -0
  179. package/build/io/hex.d.ts.map +1 -0
  180. package/build/io/hex.js +138 -0
  181. package/build/io/hex.js.map +1 -0
  182. package/build/io/index.d.ts +17 -0
  183. package/build/io/index.d.ts.map +1 -0
  184. package/build/io/index.js +23 -0
  185. package/build/io/index.js.map +1 -0
  186. package/build/io/utils.d.ts +199 -0
  187. package/build/io/utils.d.ts.map +1 -0
  188. package/build/io/utils.js +271 -0
  189. package/build/io/utils.js.map +1 -0
  190. package/build/merkle.d.ts +10 -1
  191. package/build/merkle.d.ts.map +1 -0
  192. package/build/merkle.js +12 -1
  193. package/build/merkle.js.map +1 -0
  194. package/build/networks.d.ts +70 -9
  195. package/build/networks.d.ts.map +1 -0
  196. package/build/networks.js +90 -4
  197. package/build/networks.js.map +1 -0
  198. package/build/opcodes.d.ts +1 -0
  199. package/build/opcodes.d.ts.map +1 -0
  200. package/build/opcodes.js +1 -0
  201. package/build/opcodes.js.map +1 -0
  202. package/build/payments/bip341.d.ts +35 -9
  203. package/build/payments/bip341.d.ts.map +1 -0
  204. package/build/payments/bip341.js +34 -15
  205. package/build/payments/bip341.js.map +1 -0
  206. package/build/payments/embed.d.ts +120 -1
  207. package/build/payments/embed.d.ts.map +1 -0
  208. package/build/payments/embed.js +215 -34
  209. package/build/payments/embed.js.map +1 -0
  210. package/build/payments/index.d.ts +17 -10
  211. package/build/payments/index.d.ts.map +1 -0
  212. package/build/payments/index.js +20 -10
  213. package/build/payments/index.js.map +1 -0
  214. package/build/payments/p2ms.d.ts +159 -1
  215. package/build/payments/p2ms.d.ts.map +1 -0
  216. package/build/payments/p2ms.js +427 -108
  217. package/build/payments/p2ms.js.map +1 -0
  218. package/build/payments/p2op.d.ts +158 -24
  219. package/build/payments/p2op.d.ts.map +1 -0
  220. package/build/payments/p2op.js +379 -93
  221. package/build/payments/p2op.js.map +1 -0
  222. package/build/payments/p2pk.d.ts +162 -1
  223. package/build/payments/p2pk.d.ts.map +1 -0
  224. package/build/payments/p2pk.js +327 -58
  225. package/build/payments/p2pk.js.map +1 -0
  226. package/build/payments/p2pkh.d.ts +185 -1
  227. package/build/payments/p2pkh.d.ts.map +1 -0
  228. package/build/payments/p2pkh.js +467 -114
  229. package/build/payments/p2pkh.js.map +1 -0
  230. package/build/payments/p2sh.d.ts +159 -1
  231. package/build/payments/p2sh.d.ts.map +1 -0
  232. package/build/payments/p2sh.js +500 -150
  233. package/build/payments/p2sh.js.map +1 -0
  234. package/build/payments/p2tr.d.ts +193 -1
  235. package/build/payments/p2tr.d.ts.map +1 -0
  236. package/build/payments/p2tr.js +592 -174
  237. package/build/payments/p2tr.js.map +1 -0
  238. package/build/payments/p2wpkh.d.ts +170 -1
  239. package/build/payments/p2wpkh.d.ts.map +1 -0
  240. package/build/payments/p2wpkh.js +428 -103
  241. package/build/payments/p2wpkh.js.map +1 -0
  242. package/build/payments/p2wsh.d.ts +155 -1
  243. package/build/payments/p2wsh.d.ts.map +1 -0
  244. package/build/payments/p2wsh.js +465 -143
  245. package/build/payments/p2wsh.js.map +1 -0
  246. package/build/payments/types.d.ts +98 -64
  247. package/build/payments/types.d.ts.map +1 -0
  248. package/build/payments/types.js +17 -13
  249. package/build/payments/types.js.map +1 -0
  250. package/build/psbt/bip371.d.ts +35 -9
  251. package/build/psbt/bip371.d.ts.map +1 -0
  252. package/build/psbt/bip371.js +117 -28
  253. package/build/psbt/bip371.js.map +1 -0
  254. package/build/psbt/psbtutils.d.ts +56 -16
  255. package/build/psbt/psbtutils.d.ts.map +1 -0
  256. package/build/psbt/psbtutils.js +71 -16
  257. package/build/psbt/psbtutils.js.map +1 -0
  258. package/build/psbt/types.d.ts +249 -0
  259. package/build/psbt/types.d.ts.map +1 -0
  260. package/build/psbt/types.js +6 -0
  261. package/build/psbt/types.js.map +1 -0
  262. package/build/psbt/utils.d.ts +68 -0
  263. package/build/psbt/utils.d.ts.map +1 -0
  264. package/build/psbt/utils.js +171 -0
  265. package/build/psbt/utils.js.map +1 -0
  266. package/build/psbt/validation.d.ts +88 -0
  267. package/build/psbt/validation.d.ts.map +1 -0
  268. package/build/psbt/validation.js +149 -0
  269. package/build/psbt/validation.js.map +1 -0
  270. package/build/psbt.d.ts +84 -120
  271. package/build/psbt.d.ts.map +1 -0
  272. package/build/psbt.js +406 -413
  273. package/build/psbt.js.map +1 -0
  274. package/build/pubkey.d.ts +27 -6
  275. package/build/pubkey.d.ts.map +1 -0
  276. package/build/pubkey.js +36 -12
  277. package/build/pubkey.js.map +1 -0
  278. package/build/push_data.d.ts +24 -2
  279. package/build/push_data.d.ts.map +1 -0
  280. package/build/push_data.js +44 -12
  281. package/build/push_data.js.map +1 -0
  282. package/build/script.d.ts +33 -8
  283. package/build/script.d.ts.map +1 -0
  284. package/build/script.js +101 -37
  285. package/build/script.js.map +1 -0
  286. package/build/script_number.d.ts +17 -0
  287. package/build/script_number.d.ts.map +1 -0
  288. package/build/script_number.js +19 -0
  289. package/build/script_number.js.map +1 -0
  290. package/build/script_signature.d.ts +23 -5
  291. package/build/script_signature.d.ts.map +1 -0
  292. package/build/script_signature.js +48 -15
  293. package/build/script_signature.js.map +1 -0
  294. package/build/transaction.d.ts +160 -18
  295. package/build/transaction.d.ts.map +1 -0
  296. package/build/transaction.js +443 -176
  297. package/build/transaction.js.map +1 -0
  298. package/build/tsconfig.build.tsbuildinfo +1 -0
  299. package/build/types.d.ts +36 -38
  300. package/build/types.d.ts.map +1 -0
  301. package/build/types.js +169 -57
  302. package/build/types.js.map +1 -0
  303. package/build/workers/WorkerSigningPool.d.ts +174 -0
  304. package/build/workers/WorkerSigningPool.d.ts.map +1 -0
  305. package/build/workers/WorkerSigningPool.js +553 -0
  306. package/build/workers/WorkerSigningPool.js.map +1 -0
  307. package/build/workers/WorkerSigningPool.node.d.ts +124 -0
  308. package/build/workers/WorkerSigningPool.node.d.ts.map +1 -0
  309. package/build/workers/WorkerSigningPool.node.js +753 -0
  310. package/build/workers/WorkerSigningPool.node.js.map +1 -0
  311. package/build/workers/ecc-bundle.d.ts +25 -0
  312. package/build/workers/ecc-bundle.d.ts.map +1 -0
  313. package/build/workers/ecc-bundle.js +25 -0
  314. package/build/workers/ecc-bundle.js.map +1 -0
  315. package/build/workers/index.d.ts +91 -0
  316. package/build/workers/index.d.ts.map +1 -0
  317. package/build/workers/index.js +114 -0
  318. package/build/workers/index.js.map +1 -0
  319. package/build/workers/psbt-parallel.d.ts +117 -0
  320. package/build/workers/psbt-parallel.d.ts.map +1 -0
  321. package/build/workers/psbt-parallel.js +233 -0
  322. package/build/workers/psbt-parallel.js.map +1 -0
  323. package/build/workers/signing-worker.d.ts +37 -0
  324. package/build/workers/signing-worker.d.ts.map +1 -0
  325. package/build/workers/signing-worker.js +350 -0
  326. package/build/workers/signing-worker.js.map +1 -0
  327. package/build/workers/types.d.ts +365 -0
  328. package/build/workers/types.d.ts.map +1 -0
  329. package/build/workers/types.js +60 -0
  330. package/build/workers/types.js.map +1 -0
  331. package/package.json +68 -9
  332. package/scripts/bundle-ecc.ts +111 -0
  333. package/src/address.ts +91 -45
  334. package/src/bech32utils.ts +3 -3
  335. package/src/bip66.ts +34 -24
  336. package/src/block.ts +205 -86
  337. package/src/branded.ts +18 -0
  338. package/src/crypto.ts +64 -26
  339. package/src/ecc/context.ts +280 -0
  340. package/src/ecc/index.ts +14 -0
  341. package/src/ecc/types.ts +147 -0
  342. package/src/ecpair.d.ts +99 -0
  343. package/src/errors.ts +163 -0
  344. package/src/index.ts +112 -9
  345. package/src/io/BinaryReader.ts +461 -0
  346. package/src/io/BinaryWriter.ts +696 -0
  347. package/src/io/MemoryPool.ts +343 -0
  348. package/src/io/base64.ts +20 -0
  349. package/src/io/hex.ts +155 -0
  350. package/src/io/index.ts +41 -0
  351. package/src/io/utils.ts +283 -0
  352. package/src/merkle.ts +14 -9
  353. package/src/networks.ts +9 -9
  354. package/src/payments/bip341.ts +32 -33
  355. package/src/payments/embed.ts +244 -41
  356. package/src/payments/index.ts +12 -10
  357. package/src/payments/p2ms.ts +497 -118
  358. package/src/payments/p2op.ts +432 -134
  359. package/src/payments/p2pk.ts +370 -72
  360. package/src/payments/p2pkh.ts +524 -130
  361. package/src/payments/p2sh.ts +572 -169
  362. package/src/payments/p2tr.ts +686 -194
  363. package/src/payments/p2wpkh.ts +482 -105
  364. package/src/payments/p2wsh.ts +524 -162
  365. package/src/payments/types.ts +80 -66
  366. package/src/psbt/bip371.ts +72 -51
  367. package/src/psbt/psbtutils.ts +39 -40
  368. package/src/psbt/types.ts +324 -0
  369. package/src/psbt/utils.ts +188 -0
  370. package/src/psbt/validation.ts +185 -0
  371. package/src/psbt.ts +608 -827
  372. package/src/pubkey.ts +22 -23
  373. package/src/push_data.ts +18 -16
  374. package/src/script.ts +81 -66
  375. package/src/script_number.ts +6 -6
  376. package/src/script_signature.ts +33 -36
  377. package/src/transaction.ts +462 -239
  378. package/src/types.ts +229 -100
  379. package/src/workers/WorkerSigningPool.node.ts +887 -0
  380. package/src/workers/WorkerSigningPool.ts +666 -0
  381. package/src/workers/ecc-bundle.ts +26 -0
  382. package/src/workers/index.ts +165 -0
  383. package/src/workers/psbt-parallel.ts +327 -0
  384. package/src/workers/signing-worker.ts +353 -0
  385. package/src/workers/types.ts +417 -0
  386. package/test/address.spec.ts +9 -6
  387. package/test/bitcoin.core.spec.ts +16 -17
  388. package/test/block.spec.ts +8 -7
  389. package/test/bufferutils.spec.ts +228 -214
  390. package/test/crypto.spec.ts +19 -11
  391. package/test/fixtures/p2pk.json +0 -8
  392. package/test/fixtures/p2pkh.json +1 -1
  393. package/test/fixtures/p2sh.json +1 -1
  394. package/test/fixtures/script.json +1 -1
  395. package/test/fixtures/transaction.json +2 -2
  396. package/test/integration/_regtest.ts +25 -0
  397. package/test/integration/addresses.spec.ts +4 -3
  398. package/test/integration/bip32.spec.ts +2 -1
  399. package/test/integration/blocks.spec.ts +1 -1
  400. package/test/integration/cltv.spec.ts +18 -16
  401. package/test/integration/csv.spec.ts +37 -64
  402. package/test/integration/payments.spec.ts +5 -3
  403. package/test/integration/taproot.spec.ts +76 -83
  404. package/test/integration/transactions.spec.ts +38 -35
  405. package/test/payments.spec.ts +35 -13
  406. package/test/payments.utils.ts +17 -16
  407. package/test/psbt.spec.ts +111 -100
  408. package/test/script.spec.ts +11 -10
  409. package/test/script_signature.spec.ts +9 -11
  410. package/test/taproot-cache.spec.ts +694 -0
  411. package/test/transaction.spec.ts +32 -40
  412. package/test/types.spec.ts +74 -29
  413. package/test/workers-pool.spec.ts +963 -0
  414. package/test/workers-signing.spec.ts +635 -0
  415. package/test/workers.spec.ts +1390 -0
  416. package/tsconfig.base.json +34 -18
  417. package/tsconfig.browser.json +15 -0
  418. package/tsconfig.build.json +5 -0
  419. package/tsconfig.json +5 -14
  420. package/typedoc.json +29 -0
  421. package/vite.config.browser.ts +3 -42
  422. package/vitest.config.integration.ts +2 -0
  423. package/browser/bufferutils.d.ts +0 -34
  424. package/browser/chunks/crypto-BhCpKpek.js +0 -2033
  425. package/browser/chunks/payments-B1wlSccx.js +0 -1089
  426. package/browser/chunks/psbt-BCNk7JUx.js +0 -4055
  427. package/browser/chunks/script-DyPItFEl.js +0 -318
  428. package/browser/chunks/transaction-C_UbhMGn.js +0 -432
  429. package/browser/chunks/utils-DNZi-T5W.js +0 -761
  430. package/browser/ecc_lib.d.ts +0 -3
  431. package/browser/hooks/AdvancedSignatureManager.d.ts +0 -16
  432. package/browser/hooks/HookedSigner.d.ts +0 -4
  433. package/browser/hooks/SignatureManager.d.ts +0 -13
  434. package/browser/payments/lazy.d.ts +0 -2
  435. package/browser/typeforce.d.ts +0 -38
  436. package/build/bufferutils.d.ts +0 -34
  437. package/build/bufferutils.js +0 -141
  438. package/build/ecc_lib.d.ts +0 -3
  439. package/build/ecc_lib.js +0 -61
  440. package/build/hooks/AdvancedSignatureManager.d.ts +0 -16
  441. package/build/hooks/AdvancedSignatureManager.js +0 -52
  442. package/build/hooks/HookedSigner.d.ts +0 -4
  443. package/build/hooks/HookedSigner.js +0 -64
  444. package/build/hooks/SignatureManager.d.ts +0 -13
  445. package/build/hooks/SignatureManager.js +0 -45
  446. package/build/payments/lazy.d.ts +0 -2
  447. package/build/payments/lazy.js +0 -28
  448. package/build/tsconfig.tsbuildinfo +0 -1
  449. package/src/bufferutils.ts +0 -188
  450. package/src/ecc_lib.ts +0 -94
  451. package/src/hooks/AdvancedSignatureManager.ts +0 -104
  452. package/src/hooks/HookedSigner.ts +0 -108
  453. package/src/hooks/SignatureManager.ts +0 -84
  454. package/src/payments/lazy.ts +0 -28
  455. package/src/typeforce.d.ts +0 -38
  456. package/tsconfig.webpack.json +0 -18
@@ -0,0 +1,887 @@
1
+ /**
2
+ * Node.js-specific worker signing pool implementation.
3
+ *
4
+ * Uses worker_threads module for true parallel execution.
5
+ * Private keys are isolated per-worker and zeroed immediately after signing.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import { isMainThread, Worker } from 'worker_threads';
11
+ import { cpus } from 'os';
12
+ import type {
13
+ BatchSigningMessage,
14
+ BatchSigningResultMessage,
15
+ BatchSigningTask,
16
+ ParallelSignerKeyPair,
17
+ ParallelSigningResult,
18
+ PooledWorker,
19
+ SigningResultMessage,
20
+ SigningTask,
21
+ WorkerPoolConfig,
22
+ WorkerResponse,
23
+ } from './types.js';
24
+ import { isBatchResult, isWorkerReady, WorkerState } from './types.js';
25
+
26
+ /**
27
+ * ECC library types for Node.js worker.
28
+ */
29
+ export const NodeEccLibrary = {
30
+ /** Pure JS @noble/secp256k1 (default, ~12KB) */
31
+ Noble: 'noble',
32
+ /** WASM-based tiny-secp256k1 (faster, ~1.2MB) */
33
+ TinySecp256k1: 'tiny-secp256k1',
34
+ } as const;
35
+
36
+ export type NodeEccLibrary = (typeof NodeEccLibrary)[keyof typeof NodeEccLibrary];
37
+
38
+ /**
39
+ * Extended configuration for Node.js worker pool.
40
+ */
41
+ export interface NodeWorkerPoolConfig extends WorkerPoolConfig {
42
+ /**
43
+ * ECC library type for signing.
44
+ * - NodeEccLibrary.Noble: Pure JS @noble/secp256k1 (default, ~12KB)
45
+ * - NodeEccLibrary.TinySecp256k1: WASM-based tiny-secp256k1 (faster)
46
+ *
47
+ * Default: NodeEccLibrary.Noble
48
+ */
49
+ readonly eccLibrary?: NodeEccLibrary;
50
+ }
51
+
52
+ /**
53
+ * Default configuration values for Node.js.
54
+ */
55
+ const DEFAULT_CONFIG: Required<NodeWorkerPoolConfig> = {
56
+ workerCount: cpus().length,
57
+ taskTimeoutMs: 30000,
58
+ maxKeyHoldTimeMs: 5000,
59
+ verifySignatures: true,
60
+ preserveWorkers: false,
61
+ eccLibrary: NodeEccLibrary.TinySecp256k1,
62
+ };
63
+
64
+ /**
65
+ * Pending batch awaiting completion.
66
+ */
67
+ interface PendingBatch {
68
+ readonly batchId: string;
69
+ readonly resolve: (result: BatchSigningResultMessage) => void;
70
+ readonly reject: (error: Error) => void;
71
+ readonly timeoutId: ReturnType<typeof setTimeout>;
72
+ }
73
+
74
+ /**
75
+ * Node.js-specific pooled worker.
76
+ */
77
+ interface NodePooledWorker extends Omit<PooledWorker, 'worker'> {
78
+ readonly worker: Worker;
79
+ }
80
+
81
+ /**
82
+ * Worker-based parallel signing pool for Node.js.
83
+ *
84
+ * Uses worker_threads for true parallel execution.
85
+ * Provides secure key handling with immediate zeroing after use.
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * import { NodeWorkerSigningPool } from '@btc-vision/bitcoin/workers';
90
+ *
91
+ * // Initialize pool at app startup
92
+ * const pool = NodeWorkerSigningPool.getInstance({ workerCount: 4 });
93
+ * pool.preserveWorkers();
94
+ *
95
+ * // Sign batch
96
+ * const result = await pool.signBatch(tasks, keyPair);
97
+ *
98
+ * // Cleanup at app shutdown
99
+ * await pool.shutdown();
100
+ * ```
101
+ */
102
+ export class NodeWorkerSigningPool {
103
+ /**
104
+ * Singleton instance.
105
+ */
106
+ static #instance: NodeWorkerSigningPool | null = null;
107
+
108
+ /**
109
+ * Pool configuration.
110
+ */
111
+ readonly #config: Required<NodeWorkerPoolConfig>;
112
+
113
+ /**
114
+ * Worker pool.
115
+ */
116
+ readonly #workers: NodePooledWorker[] = [];
117
+
118
+ /**
119
+ * Pending batches awaiting completion.
120
+ */
121
+ readonly #pendingBatches: Map<string, PendingBatch> = new Map();
122
+
123
+ /**
124
+ * Worker script as data URL.
125
+ */
126
+ #workerScript: string | null = null;
127
+
128
+ /**
129
+ * Whether workers are preserved between batches.
130
+ */
131
+ #preserveWorkers: boolean = false;
132
+
133
+ /**
134
+ * Next worker ID counter.
135
+ */
136
+ #nextWorkerId: number = 0;
137
+
138
+ /**
139
+ * Next task ID counter.
140
+ */
141
+ #nextTaskId: number = 0;
142
+
143
+ /**
144
+ * Whether the pool is initialized.
145
+ */
146
+ #initialized: boolean = false;
147
+
148
+ /**
149
+ * Whether the pool is shutting down.
150
+ */
151
+ #shuttingDown: boolean = false;
152
+
153
+ /**
154
+ * Creates a new NodeWorkerSigningPool.
155
+ *
156
+ * @param config - Pool configuration
157
+ */
158
+ private constructor(config: NodeWorkerPoolConfig = {}) {
159
+ if (!isMainThread) {
160
+ throw new Error('NodeWorkerSigningPool can only be created in the main thread');
161
+ }
162
+ this.#config = { ...DEFAULT_CONFIG, ...config };
163
+ this.#preserveWorkers = this.#config.preserveWorkers;
164
+ }
165
+
166
+ /**
167
+ * Number of workers in the pool.
168
+ */
169
+ public get workerCount(): number {
170
+ return this.#workers.length;
171
+ }
172
+
173
+ /**
174
+ * Number of idle workers available.
175
+ */
176
+ public get idleWorkerCount(): number {
177
+ return this.#workers.filter((w) => w.state === WorkerState.Idle).length;
178
+ }
179
+
180
+ /**
181
+ * Number of busy workers.
182
+ */
183
+ public get busyWorkerCount(): number {
184
+ return this.#workers.filter((w) => w.state === WorkerState.Busy).length;
185
+ }
186
+
187
+ /**
188
+ * Whether workers are being preserved between batches.
189
+ */
190
+ public get isPreservingWorkers(): boolean {
191
+ return this.#preserveWorkers;
192
+ }
193
+
194
+ /**
195
+ * Gets the singleton pool instance.
196
+ *
197
+ * @param config - Optional configuration (only used on first call)
198
+ * @returns The singleton pool instance
199
+ */
200
+ public static getInstance(config?: NodeWorkerPoolConfig): NodeWorkerSigningPool {
201
+ if (!NodeWorkerSigningPool.#instance) {
202
+ NodeWorkerSigningPool.#instance = new NodeWorkerSigningPool(config);
203
+ }
204
+ return NodeWorkerSigningPool.#instance;
205
+ }
206
+
207
+ /**
208
+ * Resets the singleton instance (for testing).
209
+ */
210
+ public static resetInstance(): void {
211
+ if (NodeWorkerSigningPool.#instance) {
212
+ NodeWorkerSigningPool.#instance.shutdown().catch(() => {});
213
+ NodeWorkerSigningPool.#instance = null;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Enables worker preservation between signing batches.
219
+ */
220
+ public preserveWorkers(): void {
221
+ this.#preserveWorkers = true;
222
+ }
223
+
224
+ /**
225
+ * Disables worker preservation.
226
+ */
227
+ public releaseWorkers(): void {
228
+ this.#preserveWorkers = false;
229
+ }
230
+
231
+ /**
232
+ * Initializes the worker pool.
233
+ *
234
+ * @returns Promise that resolves when all workers are ready
235
+ */
236
+ public async initialize(): Promise<void> {
237
+ if (this.#initialized) {
238
+ return;
239
+ }
240
+
241
+ if (this.#shuttingDown) {
242
+ throw new Error('Cannot initialize pool while shutting down');
243
+ }
244
+
245
+ // Create inline worker script
246
+ this.#workerScript = this.#createWorkerScript();
247
+
248
+ // Create workers
249
+ const workerPromises: Promise<void>[] = [];
250
+ for (let i = 0; i < this.#config.workerCount; i++) {
251
+ workerPromises.push(this.#createWorker());
252
+ }
253
+
254
+ await Promise.all(workerPromises);
255
+ this.#initialized = true;
256
+ }
257
+
258
+ /**
259
+ * Signs a batch of tasks in parallel.
260
+ *
261
+ * Tasks are distributed across workers and processed in batches for efficiency.
262
+ *
263
+ * @param tasks - Signing tasks
264
+ * @param keyPair - Key pair with getPrivateKey() method
265
+ * @returns Promise resolving to signing results
266
+ */
267
+ public async signBatch(
268
+ tasks: readonly SigningTask[],
269
+ keyPair: ParallelSignerKeyPair,
270
+ ): Promise<ParallelSigningResult> {
271
+ const startTime = performance.now();
272
+
273
+ // Initialize if needed
274
+ if (!this.#initialized) {
275
+ await this.initialize();
276
+ }
277
+
278
+ if (tasks.length === 0) {
279
+ return {
280
+ success: true,
281
+ signatures: new Map(),
282
+ errors: new Map(),
283
+ durationMs: performance.now() - startTime,
284
+ };
285
+ }
286
+
287
+ // Distribute tasks across workers
288
+ const workerCount = Math.min(this.#workers.length, tasks.length);
289
+ const taskBatches: SigningTask[][] = Array.from({ length: workerCount }, () => []);
290
+
291
+ for (let i = 0; i < tasks.length; i++) {
292
+ taskBatches[i % workerCount]!.push(tasks[i]!);
293
+ }
294
+
295
+ // Get private key once
296
+ const privateKey = keyPair.getPrivateKey();
297
+
298
+ try {
299
+ // Send batches to workers in parallel
300
+ const batchResults = await Promise.allSettled(
301
+ taskBatches.map((batch, index) =>
302
+ this.#signBatchOnWorker(batch, privateKey, keyPair.publicKey, index),
303
+ ),
304
+ );
305
+
306
+ // Collect all results
307
+ const signatures = new Map<number, SigningResultMessage>();
308
+ const errors = new Map<number, string>();
309
+
310
+ for (const result of batchResults) {
311
+ if (result.status === 'fulfilled') {
312
+ const batchResult = result.value;
313
+
314
+ // Add successful signatures
315
+ for (const sig of batchResult.results) {
316
+ signatures.set(sig.inputIndex, {
317
+ type: 'result',
318
+ taskId: sig.taskId,
319
+ signature: sig.signature,
320
+ inputIndex: sig.inputIndex,
321
+ publicKey: sig.publicKey,
322
+ signatureType: sig.signatureType,
323
+ leafHash: sig.leafHash,
324
+ });
325
+ }
326
+
327
+ // Add errors
328
+ for (const err of batchResult.errors) {
329
+ errors.set(err.inputIndex, err.error);
330
+ }
331
+ } else {
332
+ // Entire batch failed
333
+ const reason = result.reason as { message?: string } | undefined;
334
+ const errorMsg = reason?.message ?? 'Batch signing failed';
335
+ console.error('Batch signing failed:', errorMsg);
336
+ }
337
+ }
338
+
339
+ // Cleanup workers if not preserving
340
+ if (!this.#preserveWorkers) {
341
+ await this.#terminateIdleWorkers();
342
+ }
343
+
344
+ return {
345
+ success: errors.size === 0,
346
+ signatures,
347
+ errors,
348
+ durationMs: performance.now() - startTime,
349
+ };
350
+ } finally {
351
+ // SECURITY: Zero the key in main thread
352
+ privateKey.fill(0);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Shuts down the pool and terminates all workers.
358
+ *
359
+ * @returns Promise that resolves when all workers are terminated
360
+ */
361
+ public async shutdown(): Promise<void> {
362
+ if (this.#shuttingDown) {
363
+ return;
364
+ }
365
+
366
+ this.#shuttingDown = true;
367
+
368
+ // Terminate all workers
369
+ const terminatePromises = this.#workers.map((worker) => this.#terminateWorker(worker));
370
+
371
+ await Promise.all(terminatePromises);
372
+
373
+ // Clear state
374
+ this.#workers.length = 0;
375
+ this.#pendingBatches.clear();
376
+ this.#workerScript = null;
377
+ this.#initialized = false;
378
+ this.#shuttingDown = false;
379
+ }
380
+
381
+ /**
382
+ * Creates the inline worker script for Node.js worker_threads.
383
+ * Supports both @noble/secp256k1 (pure JS) and tiny-secp256k1 (WASM).
384
+ */
385
+ #createWorkerScript(): string {
386
+ // Node.js worker_threads can directly require/import modules
387
+ const workerCode = `
388
+ const { parentPort } = require('worker_threads');
389
+
390
+ /**
391
+ * Zero out a Uint8Array to clear sensitive data.
392
+ */
393
+ function secureZero(arr) {
394
+ if (arr && arr.fill) {
395
+ arr.fill(0);
396
+ // Double-write to prevent optimization
397
+ for (let i = 0; i < arr.length; i++) {
398
+ arr[i] = 0;
399
+ }
400
+ }
401
+ }
402
+
403
+ /**
404
+ * ECC library reference.
405
+ */
406
+ let eccLib = null;
407
+
408
+ /**
409
+ * Initialize ECC library based on type.
410
+ */
411
+ async function initEcc(eccType) {
412
+ if (eccType === '${NodeEccLibrary.TinySecp256k1}') {
413
+ // Load tiny-secp256k1 (WASM-based, faster for batch operations)
414
+ const tinysecp = await import('tiny-secp256k1');
415
+ eccLib = {
416
+ sign: (hash, privateKey) => {
417
+ return tinysecp.sign(hash, privateKey);
418
+ },
419
+ signSchnorr: (hash, privateKey) => {
420
+ return tinysecp.signSchnorr(hash, privateKey);
421
+ }
422
+ };
423
+ } else {
424
+ // Default to @noble/secp256k1 (pure JS, no WASM)
425
+ const noble = await import('@noble/secp256k1');
426
+ eccLib = {
427
+ sign: (hash, privateKey) => {
428
+ // noble returns Signature object, we need raw bytes
429
+ const sig = noble.sign(hash, privateKey, { lowS: true });
430
+ return sig.toCompactRawBytes();
431
+ },
432
+ signSchnorr: (hash, privateKey) => {
433
+ return noble.schnorr.sign(hash, privateKey);
434
+ }
435
+ };
436
+ }
437
+ }
438
+
439
+ parentPort.on('message', async (msg) => {
440
+ switch (msg.type) {
441
+ case 'init':
442
+ try {
443
+ await initEcc(msg.eccType || 'tiny-secp256k1');
444
+ parentPort.postMessage({ type: 'ready' });
445
+ } catch (error) {
446
+ parentPort.postMessage({
447
+ type: 'error',
448
+ taskId: 'init',
449
+ error: 'Failed to load ECC library: ' + (error.message || error),
450
+ inputIndex: -1
451
+ });
452
+ }
453
+ break;
454
+
455
+ case 'sign':
456
+ handleSign(msg);
457
+ break;
458
+
459
+ case 'signBatch':
460
+ handleSignBatch(msg);
461
+ break;
462
+
463
+ case 'shutdown':
464
+ parentPort.postMessage({ type: 'shutdown-ack' });
465
+ process.exit(0);
466
+ break;
467
+
468
+ default:
469
+ parentPort.postMessage({
470
+ type: 'error',
471
+ taskId: msg.taskId || 'unknown',
472
+ error: 'Unknown message type: ' + msg.type,
473
+ inputIndex: msg.inputIndex || -1
474
+ });
475
+ }
476
+ });
477
+
478
+ function handleSign(msg) {
479
+ const {
480
+ taskId,
481
+ hash,
482
+ privateKey,
483
+ publicKey,
484
+ signatureType,
485
+ lowR,
486
+ inputIndex,
487
+ sighashType,
488
+ leafHash
489
+ } = msg;
490
+
491
+ // Validate inputs
492
+ if (!hash || hash.length !== 32) {
493
+ secureZero(privateKey);
494
+ parentPort.postMessage({
495
+ type: 'error',
496
+ taskId: taskId,
497
+ error: 'Invalid hash: must be 32 bytes',
498
+ inputIndex: inputIndex
499
+ });
500
+ return;
501
+ }
502
+
503
+ if (!privateKey || privateKey.length !== 32) {
504
+ secureZero(privateKey);
505
+ parentPort.postMessage({
506
+ type: 'error',
507
+ taskId: taskId,
508
+ error: 'Invalid private key: must be 32 bytes',
509
+ inputIndex: inputIndex
510
+ });
511
+ return;
512
+ }
513
+
514
+ if (!eccLib) {
515
+ secureZero(privateKey);
516
+ parentPort.postMessage({
517
+ type: 'error',
518
+ taskId: taskId,
519
+ error: 'ECC library not initialized. Call init first.',
520
+ inputIndex: inputIndex
521
+ });
522
+ return;
523
+ }
524
+
525
+ let signature;
526
+
527
+ try {
528
+ if (signatureType === 1) {
529
+ // Schnorr signature (BIP340)
530
+ signature = eccLib.signSchnorr(hash, privateKey);
531
+ } else {
532
+ // ECDSA signature
533
+ signature = eccLib.sign(hash, privateKey, { lowR: lowR || false });
534
+ }
535
+
536
+ if (!signature) {
537
+ throw new Error('Signing returned null or undefined');
538
+ }
539
+
540
+ } catch (error) {
541
+ secureZero(privateKey);
542
+ parentPort.postMessage({
543
+ type: 'error',
544
+ taskId: taskId,
545
+ error: error.message || 'Signing failed',
546
+ inputIndex: inputIndex
547
+ });
548
+ return;
549
+ }
550
+
551
+ // CRITICAL: Zero the private key immediately
552
+ secureZero(privateKey);
553
+
554
+ const result = {
555
+ type: 'result',
556
+ taskId: taskId,
557
+ signature: signature,
558
+ inputIndex: inputIndex,
559
+ publicKey: publicKey,
560
+ signatureType: signatureType
561
+ };
562
+
563
+ if (leafHash) {
564
+ result.leafHash = leafHash;
565
+ }
566
+
567
+ parentPort.postMessage(result);
568
+ }
569
+
570
+ function handleSignBatch(msg) {
571
+ const { batchId, tasks, privateKey } = msg;
572
+ const results = [];
573
+ const errors = [];
574
+
575
+ // Validate private key once
576
+ if (!privateKey || privateKey.length !== 32) {
577
+ secureZero(privateKey);
578
+ parentPort.postMessage({
579
+ type: 'batchResult',
580
+ batchId: batchId,
581
+ results: [],
582
+ errors: [{ inputIndex: -1, error: 'Invalid private key: must be 32 bytes' }]
583
+ });
584
+ return;
585
+ }
586
+
587
+ if (!eccLib) {
588
+ secureZero(privateKey);
589
+ parentPort.postMessage({
590
+ type: 'batchResult',
591
+ batchId: batchId,
592
+ results: [],
593
+ errors: [{ inputIndex: -1, error: 'ECC library not initialized. Call init first.' }]
594
+ });
595
+ return;
596
+ }
597
+
598
+ // Process all tasks
599
+ for (const task of tasks) {
600
+ const { taskId, hash, publicKey, signatureType, lowR, inputIndex, sighashType, leafHash } = task;
601
+
602
+ // Validate hash
603
+ if (!hash || hash.length !== 32) {
604
+ errors.push({ taskId, inputIndex, error: 'Invalid hash: must be 32 bytes' });
605
+ continue;
606
+ }
607
+
608
+ try {
609
+ let signature;
610
+ if (signatureType === 1) {
611
+ // Schnorr signature (BIP340)
612
+ signature = eccLib.signSchnorr(hash, privateKey);
613
+ } else {
614
+ // ECDSA signature
615
+ signature = eccLib.sign(hash, privateKey, { lowR: lowR || false });
616
+ }
617
+
618
+ if (!signature) {
619
+ throw new Error('Signing returned null or undefined');
620
+ }
621
+
622
+ const result = {
623
+ taskId: taskId,
624
+ signature: signature,
625
+ inputIndex: inputIndex,
626
+ publicKey: publicKey,
627
+ signatureType: signatureType
628
+ };
629
+
630
+ if (leafHash) {
631
+ result.leafHash = leafHash;
632
+ }
633
+
634
+ results.push(result);
635
+ } catch (error) {
636
+ errors.push({ taskId, inputIndex, error: error.message || 'Signing failed' });
637
+ }
638
+ }
639
+
640
+ // CRITICAL: Zero the private key after processing all tasks
641
+ secureZero(privateKey);
642
+
643
+ // Send batch result back
644
+ parentPort.postMessage({
645
+ type: 'batchResult',
646
+ batchId: batchId,
647
+ results: results,
648
+ errors: errors
649
+ });
650
+ }
651
+ `;
652
+ return workerCode;
653
+ }
654
+
655
+ /**
656
+ * Creates a new worker and adds it to the pool.
657
+ */
658
+ async #createWorker(): Promise<void> {
659
+ if (!this.#workerScript) {
660
+ throw new Error('Worker script not created');
661
+ }
662
+
663
+ const workerId = this.#nextWorkerId++;
664
+
665
+ // Create worker with eval code
666
+ const worker = new Worker(this.#workerScript, {
667
+ eval: true,
668
+ name: `signing-worker-${workerId}`,
669
+ });
670
+
671
+ const pooledWorker: NodePooledWorker = {
672
+ id: workerId,
673
+ state: WorkerState.Initializing,
674
+ worker,
675
+ currentTaskId: null,
676
+ taskStartTime: null,
677
+ };
678
+
679
+ this.#workers.push(pooledWorker);
680
+
681
+ // Wait for worker to be ready
682
+ await new Promise<void>((resolve, reject) => {
683
+ const timeout = setTimeout(() => {
684
+ reject(new Error(`Worker ${workerId} initialization timeout`));
685
+ }, 10000);
686
+
687
+ const messageHandler = (data: WorkerResponse): void => {
688
+ if (isWorkerReady(data)) {
689
+ clearTimeout(timeout);
690
+ worker.off('message', messageHandler);
691
+ pooledWorker.state = WorkerState.Idle;
692
+ resolve();
693
+ }
694
+ };
695
+
696
+ worker.on('message', messageHandler);
697
+ worker.on('error', (error: Error) => {
698
+ clearTimeout(timeout);
699
+ reject(new Error(`Worker ${workerId} error: ${error.message}`));
700
+ });
701
+
702
+ // Send init message with ECC library type
703
+ worker.postMessage({
704
+ type: 'init',
705
+ eccType: this.#config.eccLibrary,
706
+ });
707
+ });
708
+
709
+ // Set up message handler for signing results
710
+ worker.on('message', (data: WorkerResponse) => {
711
+ this.#handleWorkerMessage(pooledWorker, data);
712
+ });
713
+ }
714
+
715
+ /**
716
+ * Signs a batch of tasks on a specific worker.
717
+ */
718
+ async #signBatchOnWorker(
719
+ tasks: readonly SigningTask[],
720
+ privateKey: Uint8Array,
721
+ publicKey: Uint8Array,
722
+ workerIndex: number,
723
+ ): Promise<BatchSigningResultMessage> {
724
+ if (tasks.length === 0) {
725
+ return { type: 'batchResult', batchId: '', results: [], errors: [] };
726
+ }
727
+
728
+ // Get worker at index (or wait for idle)
729
+ const worker = this.#workers[workerIndex] ?? (await this.#getIdleWorker());
730
+
731
+ // Generate unique batch ID
732
+ const batchId = `batch-${this.#nextTaskId++}`;
733
+
734
+ return new Promise<BatchSigningResultMessage>((resolve, reject) => {
735
+ // Set up timeout
736
+ const timeoutId = setTimeout(() => {
737
+ this.#pendingBatches.delete(batchId);
738
+ worker.state = WorkerState.Idle;
739
+ worker.currentTaskId = null;
740
+ worker.taskStartTime = null;
741
+
742
+ // SECURITY: Terminate worker that exceeded key hold time
743
+ this.#terminateWorker(worker).catch(() => {});
744
+ this.#createWorker().catch(() => {});
745
+
746
+ reject(new Error(`Batch signing timeout for ${tasks.length} tasks`));
747
+ }, this.#config.maxKeyHoldTimeMs);
748
+
749
+ // Store pending batch
750
+ const pendingBatch: PendingBatch = {
751
+ batchId,
752
+ resolve,
753
+ reject,
754
+ timeoutId,
755
+ };
756
+ this.#pendingBatches.set(batchId, pendingBatch);
757
+
758
+ // Mark worker as busy
759
+ worker.state = WorkerState.Busy;
760
+ worker.currentTaskId = batchId;
761
+ worker.taskStartTime = Date.now();
762
+
763
+ // Convert tasks to batch format
764
+ const batchTasks: BatchSigningTask[] = tasks.map((task) => ({
765
+ taskId: task.taskId,
766
+ hash: task.hash,
767
+ publicKey,
768
+ signatureType: task.signatureType,
769
+ lowR: task.lowR,
770
+ inputIndex: task.inputIndex,
771
+ sighashType: task.sighashType,
772
+ leafHash: task.leafHash,
773
+ }));
774
+
775
+ // Create batch message
776
+ const message: BatchSigningMessage = {
777
+ type: 'signBatch',
778
+ batchId,
779
+ tasks: batchTasks,
780
+ privateKey,
781
+ };
782
+
783
+ // Send to worker
784
+ worker.worker.postMessage(message);
785
+ });
786
+ }
787
+
788
+ /**
789
+ * Gets an idle worker, creating one if necessary.
790
+ */
791
+ async #getIdleWorker(): Promise<NodePooledWorker> {
792
+ let worker = this.#workers.find((w) => w.state === WorkerState.Idle);
793
+
794
+ if (worker) {
795
+ return worker;
796
+ }
797
+
798
+ if (this.#workers.length < this.#config.workerCount) {
799
+ await this.#createWorker();
800
+ worker = this.#workers.find((w) => w.state === WorkerState.Idle);
801
+ if (worker) {
802
+ return worker;
803
+ }
804
+ }
805
+
806
+ return new Promise<NodePooledWorker>((resolve) => {
807
+ const checkInterval = setInterval(() => {
808
+ const idleWorker = this.#workers.find((w) => w.state === WorkerState.Idle);
809
+ if (idleWorker) {
810
+ clearInterval(checkInterval);
811
+ resolve(idleWorker);
812
+ }
813
+ }, 10);
814
+ });
815
+ }
816
+
817
+ /**
818
+ * Handles a message from a worker.
819
+ */
820
+ #handleWorkerMessage(worker: NodePooledWorker, response: WorkerResponse): void {
821
+ if (isBatchResult(response)) {
822
+ const pending = this.#pendingBatches.get(response.batchId);
823
+ if (pending) {
824
+ clearTimeout(pending.timeoutId);
825
+ this.#pendingBatches.delete(response.batchId);
826
+ worker.state = WorkerState.Idle;
827
+ worker.currentTaskId = null;
828
+ worker.taskStartTime = null;
829
+ pending.resolve(response);
830
+ }
831
+ }
832
+ // Ignore ready and shutdown-ack messages here (handled elsewhere)
833
+ }
834
+
835
+ /**
836
+ * Terminates a worker.
837
+ */
838
+ async #terminateWorker(worker: NodePooledWorker): Promise<void> {
839
+ if (worker.state === WorkerState.Terminated) {
840
+ return;
841
+ }
842
+
843
+ worker.state = WorkerState.ShuttingDown;
844
+ worker.worker.postMessage({ type: 'shutdown' });
845
+
846
+ await new Promise<void>((resolve) => {
847
+ const timeout = setTimeout(async () => {
848
+ await worker.worker.terminate();
849
+ worker.state = WorkerState.Terminated;
850
+ resolve();
851
+ }, 1000);
852
+
853
+ const handler = (data: WorkerResponse): void => {
854
+ if (data.type === 'shutdown-ack') {
855
+ clearTimeout(timeout);
856
+ worker.worker.off('message', handler);
857
+ void worker.worker.terminate().then(() => {
858
+ worker.state = WorkerState.Terminated;
859
+ resolve();
860
+ });
861
+ }
862
+ };
863
+
864
+ worker.worker.on('message', handler);
865
+ });
866
+
867
+ const index = this.#workers.indexOf(worker);
868
+ if (index >= 0) {
869
+ this.#workers.splice(index, 1);
870
+ }
871
+ }
872
+
873
+ /**
874
+ * Terminates all idle workers.
875
+ */
876
+ async #terminateIdleWorkers(): Promise<void> {
877
+ const idleWorkers = this.#workers.filter((w) => w.state === WorkerState.Idle);
878
+ await Promise.all(idleWorkers.map((w) => this.#terminateWorker(w)));
879
+ }
880
+ }
881
+
882
+ /**
883
+ * Convenience function to get the singleton pool instance.
884
+ */
885
+ export function getNodeSigningPool(config?: NodeWorkerPoolConfig): NodeWorkerSigningPool {
886
+ return NodeWorkerSigningPool.getInstance(config);
887
+ }