@bopen-io/wallet-toolbox 1.7.18

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 (390) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.env.template +22 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  4. package/.github/ISSUE_TEMPLATE/discussion.md +24 -0
  5. package/.github/pull_request_template.md +22 -0
  6. package/.github/workflows/push.yaml +145 -0
  7. package/.prettierrc +10 -0
  8. package/CHANGELOG.md +280 -0
  9. package/CONTRIBUTING.md +89 -0
  10. package/README.md +43 -0
  11. package/docs/README.md +85 -0
  12. package/docs/client.md +19627 -0
  13. package/docs/monitor.md +953 -0
  14. package/docs/open-rpc/index.html +46 -0
  15. package/docs/services.md +6377 -0
  16. package/docs/setup.md +1268 -0
  17. package/docs/storage.md +5367 -0
  18. package/docs/wallet.md +19626 -0
  19. package/jest.config.ts +25 -0
  20. package/license.md +28 -0
  21. package/out/tsconfig.all.tsbuildinfo +1 -0
  22. package/package.json +63 -0
  23. package/src/CWIStyleWalletManager.ts +1999 -0
  24. package/src/Setup.ts +579 -0
  25. package/src/SetupClient.ts +322 -0
  26. package/src/SetupWallet.ts +108 -0
  27. package/src/SimpleWalletManager.ts +526 -0
  28. package/src/Wallet.ts +1169 -0
  29. package/src/WalletAuthenticationManager.ts +153 -0
  30. package/src/WalletLogger.ts +213 -0
  31. package/src/WalletPermissionsManager.ts +3660 -0
  32. package/src/WalletSettingsManager.ts +114 -0
  33. package/src/__tests/CWIStyleWalletManager.test.d.ts.map +1 -0
  34. package/src/__tests/CWIStyleWalletManager.test.js.map +1 -0
  35. package/src/__tests/CWIStyleWalletManager.test.ts +675 -0
  36. package/src/__tests/WalletPermissionsManager.callbacks.test.ts +323 -0
  37. package/src/__tests/WalletPermissionsManager.checks.test.ts +844 -0
  38. package/src/__tests/WalletPermissionsManager.encryption.test.ts +412 -0
  39. package/src/__tests/WalletPermissionsManager.fixtures.ts +307 -0
  40. package/src/__tests/WalletPermissionsManager.flows.test.ts +462 -0
  41. package/src/__tests/WalletPermissionsManager.initialization.test.ts +300 -0
  42. package/src/__tests/WalletPermissionsManager.pmodules.test.ts +798 -0
  43. package/src/__tests/WalletPermissionsManager.proxying.test.ts +724 -0
  44. package/src/__tests/WalletPermissionsManager.tokens.test.ts +503 -0
  45. package/src/index.all.ts +27 -0
  46. package/src/index.client.ts +25 -0
  47. package/src/index.mobile.ts +21 -0
  48. package/src/index.ts +1 -0
  49. package/src/monitor/Monitor.ts +412 -0
  50. package/src/monitor/MonitorDaemon.ts +188 -0
  51. package/src/monitor/README.md +3 -0
  52. package/src/monitor/__test/MonitorDaemon.man.test.ts +45 -0
  53. package/src/monitor/tasks/TaskCheckForProofs.ts +243 -0
  54. package/src/monitor/tasks/TaskCheckNoSends.ts +73 -0
  55. package/src/monitor/tasks/TaskClock.ts +33 -0
  56. package/src/monitor/tasks/TaskFailAbandoned.ts +54 -0
  57. package/src/monitor/tasks/TaskMonitorCallHistory.ts +26 -0
  58. package/src/monitor/tasks/TaskNewHeader.ts +93 -0
  59. package/src/monitor/tasks/TaskPurge.ts +68 -0
  60. package/src/monitor/tasks/TaskReorg.ts +89 -0
  61. package/src/monitor/tasks/TaskReviewStatus.ts +48 -0
  62. package/src/monitor/tasks/TaskSendWaiting.ts +122 -0
  63. package/src/monitor/tasks/TaskSyncWhenIdle.ts +26 -0
  64. package/src/monitor/tasks/TaskUnFail.ts +151 -0
  65. package/src/monitor/tasks/WalletMonitorTask.ts +47 -0
  66. package/src/sdk/CertOpsWallet.ts +18 -0
  67. package/src/sdk/PrivilegedKeyManager.ts +372 -0
  68. package/src/sdk/README.md +13 -0
  69. package/src/sdk/WERR_errors.ts +234 -0
  70. package/src/sdk/WalletError.ts +170 -0
  71. package/src/sdk/WalletErrorFromJson.ts +80 -0
  72. package/src/sdk/WalletServices.interfaces.ts +700 -0
  73. package/src/sdk/WalletSigner.interfaces.ts +11 -0
  74. package/src/sdk/WalletStorage.interfaces.ts +606 -0
  75. package/src/sdk/__test/CertificateLifeCycle.test.ts +131 -0
  76. package/src/sdk/__test/PrivilegedKeyManager.test.ts +738 -0
  77. package/src/sdk/__test/WalletError.test.ts +318 -0
  78. package/src/sdk/__test/validationHelpers.test.ts +21 -0
  79. package/src/sdk/index.ts +10 -0
  80. package/src/sdk/types.ts +226 -0
  81. package/src/services/README.md +11 -0
  82. package/src/services/ServiceCollection.ts +248 -0
  83. package/src/services/Services.ts +603 -0
  84. package/src/services/__tests/ARC.man.test.ts +123 -0
  85. package/src/services/__tests/ARC.timeout.man.test.ts +79 -0
  86. package/src/services/__tests/ArcGorillaPool.man.test.ts +108 -0
  87. package/src/services/__tests/arcServices.test.ts +8 -0
  88. package/src/services/__tests/bitrails.test.ts +56 -0
  89. package/src/services/__tests/getMerklePath.test.ts +15 -0
  90. package/src/services/__tests/getRawTx.test.ts +13 -0
  91. package/src/services/__tests/postBeef.test.ts +104 -0
  92. package/src/services/__tests/verifyBeef.test.ts +50 -0
  93. package/src/services/chaintracker/BHServiceClient.ts +212 -0
  94. package/src/services/chaintracker/ChaintracksChainTracker.ts +71 -0
  95. package/src/services/chaintracker/__tests/ChaintracksChainTracker.test.ts +33 -0
  96. package/src/services/chaintracker/__tests/ChaintracksServiceClient.test.ts +29 -0
  97. package/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.ts +72 -0
  98. package/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.ts +83 -0
  99. package/src/services/chaintracker/chaintracks/Api/BulkStorageApi.ts +92 -0
  100. package/src/services/chaintracker/chaintracks/Api/ChaintracksApi.ts +64 -0
  101. package/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.ts +189 -0
  102. package/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.ts +18 -0
  103. package/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.ts +58 -0
  104. package/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.ts +386 -0
  105. package/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.ts +25 -0
  106. package/src/services/chaintracker/chaintracks/Chaintracks.ts +609 -0
  107. package/src/services/chaintracker/chaintracks/ChaintracksService.ts +199 -0
  108. package/src/services/chaintracker/chaintracks/ChaintracksServiceClient.ts +154 -0
  109. package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.ts +176 -0
  110. package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.ts +174 -0
  111. package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.ts +18 -0
  112. package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.ts +113 -0
  113. package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainWs.ts +81 -0
  114. package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.ts +86 -0
  115. package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorTeranodeP2P.ts +59 -0
  116. package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.ts +104 -0
  117. package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainWs.ts +66 -0
  118. package/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.ts +566 -0
  119. package/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.ts +219 -0
  120. package/src/services/chaintracker/chaintracks/Ingest/__tests/BulkIngestorCDNBabbage.test.ts +54 -0
  121. package/src/services/chaintracker/chaintracks/Ingest/__tests/LiveIngestorWhatsOnChainPoll.test.ts +33 -0
  122. package/src/services/chaintracker/chaintracks/Ingest/__tests/WhatsOnChainServices.test.ts +124 -0
  123. package/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.ts +92 -0
  124. package/src/services/chaintracker/chaintracks/Storage/ChaintracksKnexMigrations.ts +104 -0
  125. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.ts +382 -0
  126. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.ts +574 -0
  127. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.ts +438 -0
  128. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageMemory.ts +29 -0
  129. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.ts +304 -0
  130. package/src/services/chaintracker/chaintracks/Storage/__tests/ChaintracksStorageIdb.test.ts +102 -0
  131. package/src/services/chaintracker/chaintracks/Storage/__tests/ChaintracksStorageKnex.test.ts +45 -0
  132. package/src/services/chaintracker/chaintracks/__tests/Chaintracks.test.ts +77 -0
  133. package/src/services/chaintracker/chaintracks/__tests/ChaintracksClientApi.test.ts +192 -0
  134. package/src/services/chaintracker/chaintracks/__tests/LocalCdnServer.ts +75 -0
  135. package/src/services/chaintracker/chaintracks/__tests/createIdbChaintracks.test.ts +62 -0
  136. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNetBlockHeaders.json +1 -0
  137. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_0.headers +0 -0
  138. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_1.headers +0 -0
  139. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_2.headers +0 -0
  140. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_3.headers +0 -0
  141. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNetBlockHeaders.json +1 -0
  142. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_0.headers +0 -0
  143. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_1.headers +0 -0
  144. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_2.headers +0 -0
  145. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_3.headers +0 -0
  146. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNetBlockHeaders.json +1 -0
  147. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_0.headers +0 -0
  148. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_1.headers +0 -0
  149. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_2.headers +0 -0
  150. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_3.headers +0 -0
  151. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNetBlockHeaders.json +1 -0
  152. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_0.headers +0 -0
  153. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_1.headers +0 -0
  154. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_2.headers +0 -0
  155. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_3.headers +0 -0
  156. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_4.headers +0 -0
  157. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNetBlockHeaders.json +1 -0
  158. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_0.headers +0 -0
  159. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_1.headers +0 -0
  160. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_2.headers +0 -0
  161. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_3.headers +0 -0
  162. package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_4.headers +0 -0
  163. package/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.ts +92 -0
  164. package/src/services/chaintracker/chaintracks/createDefaultKnexChaintracksOptions.ts +111 -0
  165. package/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.ts +91 -0
  166. package/src/services/chaintracker/chaintracks/createIdbChaintracks.ts +60 -0
  167. package/src/services/chaintracker/chaintracks/createKnexChaintracks.ts +65 -0
  168. package/src/services/chaintracker/chaintracks/createNoDbChaintracks.ts +60 -0
  169. package/src/services/chaintracker/chaintracks/index.all.ts +12 -0
  170. package/src/services/chaintracker/chaintracks/index.client.ts +4 -0
  171. package/src/services/chaintracker/chaintracks/index.mobile.ts +37 -0
  172. package/src/services/chaintracker/chaintracks/util/BulkFileDataManager.ts +975 -0
  173. package/src/services/chaintracker/chaintracks/util/BulkFileDataReader.ts +60 -0
  174. package/src/services/chaintracker/chaintracks/util/BulkFilesReader.ts +336 -0
  175. package/src/services/chaintracker/chaintracks/util/BulkHeaderFile.ts +247 -0
  176. package/src/services/chaintracker/chaintracks/util/ChaintracksFetch.ts +69 -0
  177. package/src/services/chaintracker/chaintracks/util/ChaintracksFs.ts +141 -0
  178. package/src/services/chaintracker/chaintracks/util/HeightRange.ts +153 -0
  179. package/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.ts +76 -0
  180. package/src/services/chaintracker/chaintracks/util/__tests/BulkFileDataManager.test.ts +304 -0
  181. package/src/services/chaintracker/chaintracks/util/__tests/ChaintracksFetch.test.ts +60 -0
  182. package/src/services/chaintracker/chaintracks/util/__tests/HeightRange.test.ts +67 -0
  183. package/src/services/chaintracker/chaintracks/util/__tests/SingleWriterMultiReaderLock.test.ts +49 -0
  184. package/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.ts +573 -0
  185. package/src/services/chaintracker/chaintracks/util/dirtyHashes.ts +29 -0
  186. package/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.ts +432 -0
  187. package/src/services/chaintracker/index.all.ts +4 -0
  188. package/src/services/chaintracker/index.client.ts +4 -0
  189. package/src/services/chaintracker/index.mobile.ts +4 -0
  190. package/src/services/createDefaultWalletServicesOptions.ts +77 -0
  191. package/src/services/index.ts +1 -0
  192. package/src/services/processingErrors/arcSuccessError.json +76 -0
  193. package/src/services/providers/ARC.ts +350 -0
  194. package/src/services/providers/Bitails.ts +256 -0
  195. package/src/services/providers/SdkWhatsOnChain.ts +83 -0
  196. package/src/services/providers/WhatsOnChain.ts +883 -0
  197. package/src/services/providers/__tests/WhatsOnChain.test.ts +242 -0
  198. package/src/services/providers/__tests/exchangeRates.test.ts +18 -0
  199. package/src/services/providers/exchangeRates.ts +265 -0
  200. package/src/services/providers/getBeefForTxid.ts +369 -0
  201. package/src/signer/README.md +5 -0
  202. package/src/signer/WalletSigner.ts +17 -0
  203. package/src/signer/methods/acquireDirectCertificate.ts +52 -0
  204. package/src/signer/methods/buildSignableTransaction.ts +183 -0
  205. package/src/signer/methods/completeSignedTransaction.ts +117 -0
  206. package/src/signer/methods/createAction.ts +172 -0
  207. package/src/signer/methods/internalizeAction.ts +106 -0
  208. package/src/signer/methods/proveCertificate.ts +43 -0
  209. package/src/signer/methods/signAction.ts +54 -0
  210. package/src/storage/README.md +14 -0
  211. package/src/storage/StorageIdb.ts +2304 -0
  212. package/src/storage/StorageKnex.ts +1425 -0
  213. package/src/storage/StorageProvider.ts +810 -0
  214. package/src/storage/StorageReader.ts +194 -0
  215. package/src/storage/StorageReaderWriter.ts +432 -0
  216. package/src/storage/StorageSyncReader.ts +34 -0
  217. package/src/storage/WalletStorageManager.ts +943 -0
  218. package/src/storage/__test/StorageIdb.test.ts +43 -0
  219. package/src/storage/__test/WalletStorageManager.test.ts +275 -0
  220. package/src/storage/__test/adminStats.man.test.ts +89 -0
  221. package/src/storage/__test/getBeefForTransaction.test.ts +385 -0
  222. package/src/storage/index.all.ts +11 -0
  223. package/src/storage/index.client.ts +7 -0
  224. package/src/storage/index.mobile.ts +6 -0
  225. package/src/storage/methods/ListActionsSpecOp.ts +70 -0
  226. package/src/storage/methods/ListOutputsSpecOp.ts +129 -0
  227. package/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +1057 -0
  228. package/src/storage/methods/__test/GenerateChange/randomValsUsed1.ts +20 -0
  229. package/src/storage/methods/__test/offsetKey.test.ts +274 -0
  230. package/src/storage/methods/attemptToPostReqsToNetwork.ts +389 -0
  231. package/src/storage/methods/createAction.ts +947 -0
  232. package/src/storage/methods/generateChange.ts +556 -0
  233. package/src/storage/methods/getBeefForTransaction.ts +139 -0
  234. package/src/storage/methods/getSyncChunk.ts +293 -0
  235. package/src/storage/methods/internalizeAction.ts +562 -0
  236. package/src/storage/methods/listActionsIdb.ts +183 -0
  237. package/src/storage/methods/listActionsKnex.ts +226 -0
  238. package/src/storage/methods/listCertificates.ts +73 -0
  239. package/src/storage/methods/listOutputsIdb.ts +203 -0
  240. package/src/storage/methods/listOutputsKnex.ts +263 -0
  241. package/src/storage/methods/offsetKey.ts +89 -0
  242. package/src/storage/methods/processAction.ts +420 -0
  243. package/src/storage/methods/purgeData.ts +251 -0
  244. package/src/storage/methods/purgeDataIdb.ts +10 -0
  245. package/src/storage/methods/reviewStatus.ts +101 -0
  246. package/src/storage/methods/reviewStatusIdb.ts +43 -0
  247. package/src/storage/methods/utils.Buffer.ts +33 -0
  248. package/src/storage/methods/utils.ts +56 -0
  249. package/src/storage/remoting/StorageClient.ts +567 -0
  250. package/src/storage/remoting/StorageMobile.ts +544 -0
  251. package/src/storage/remoting/StorageServer.ts +291 -0
  252. package/src/storage/remoting/__test/StorageClient.test.ts +113 -0
  253. package/src/storage/schema/KnexMigrations.ts +489 -0
  254. package/src/storage/schema/StorageIdbSchema.ts +150 -0
  255. package/src/storage/schema/entities/EntityBase.ts +210 -0
  256. package/src/storage/schema/entities/EntityCertificate.ts +188 -0
  257. package/src/storage/schema/entities/EntityCertificateField.ts +136 -0
  258. package/src/storage/schema/entities/EntityCommission.ts +148 -0
  259. package/src/storage/schema/entities/EntityOutput.ts +290 -0
  260. package/src/storage/schema/entities/EntityOutputBasket.ts +153 -0
  261. package/src/storage/schema/entities/EntityOutputTag.ts +121 -0
  262. package/src/storage/schema/entities/EntityOutputTagMap.ts +123 -0
  263. package/src/storage/schema/entities/EntityProvenTx.ts +319 -0
  264. package/src/storage/schema/entities/EntityProvenTxReq.ts +580 -0
  265. package/src/storage/schema/entities/EntitySyncState.ts +389 -0
  266. package/src/storage/schema/entities/EntityTransaction.ts +306 -0
  267. package/src/storage/schema/entities/EntityTxLabel.ts +121 -0
  268. package/src/storage/schema/entities/EntityTxLabelMap.ts +123 -0
  269. package/src/storage/schema/entities/EntityUser.ts +112 -0
  270. package/src/storage/schema/entities/MergeEntity.ts +73 -0
  271. package/src/storage/schema/entities/__tests/CertificateFieldTests.test.ts +353 -0
  272. package/src/storage/schema/entities/__tests/CertificateTests.test.ts +354 -0
  273. package/src/storage/schema/entities/__tests/CommissionTests.test.ts +371 -0
  274. package/src/storage/schema/entities/__tests/OutputBasketTests.test.ts +278 -0
  275. package/src/storage/schema/entities/__tests/OutputTagMapTests.test.ts +242 -0
  276. package/src/storage/schema/entities/__tests/OutputTagTests.test.ts +288 -0
  277. package/src/storage/schema/entities/__tests/OutputTests.test.ts +464 -0
  278. package/src/storage/schema/entities/__tests/ProvenTxReqTests.test.ts +340 -0
  279. package/src/storage/schema/entities/__tests/ProvenTxTests.test.ts +504 -0
  280. package/src/storage/schema/entities/__tests/SyncStateTests.test.ts +288 -0
  281. package/src/storage/schema/entities/__tests/TransactionTests.test.ts +604 -0
  282. package/src/storage/schema/entities/__tests/TxLabelMapTests.test.ts +361 -0
  283. package/src/storage/schema/entities/__tests/TxLabelTests.test.ts +198 -0
  284. package/src/storage/schema/entities/__tests/stampLogTests.test.ts +90 -0
  285. package/src/storage/schema/entities/__tests/usersTests.test.ts +340 -0
  286. package/src/storage/schema/entities/index.ts +16 -0
  287. package/src/storage/schema/tables/TableCertificate.ts +21 -0
  288. package/src/storage/schema/tables/TableCertificateField.ts +12 -0
  289. package/src/storage/schema/tables/TableCommission.ts +13 -0
  290. package/src/storage/schema/tables/TableMonitorEvent.ts +9 -0
  291. package/src/storage/schema/tables/TableOutput.ts +64 -0
  292. package/src/storage/schema/tables/TableOutputBasket.ts +12 -0
  293. package/src/storage/schema/tables/TableOutputTag.ts +10 -0
  294. package/src/storage/schema/tables/TableOutputTagMap.ts +9 -0
  295. package/src/storage/schema/tables/TableProvenTx.ts +14 -0
  296. package/src/storage/schema/tables/TableProvenTxReq.ts +65 -0
  297. package/src/storage/schema/tables/TableSettings.ts +17 -0
  298. package/src/storage/schema/tables/TableSyncState.ts +18 -0
  299. package/src/storage/schema/tables/TableTransaction.ts +54 -0
  300. package/src/storage/schema/tables/TableTxLabel.ts +10 -0
  301. package/src/storage/schema/tables/TableTxLabelMap.ts +9 -0
  302. package/src/storage/schema/tables/TableUser.ts +16 -0
  303. package/src/storage/schema/tables/index.ts +16 -0
  304. package/src/storage/sync/StorageMySQLDojoReader.ts +696 -0
  305. package/src/storage/sync/index.ts +1 -0
  306. package/src/utility/Format.ts +133 -0
  307. package/src/utility/README.md +3 -0
  308. package/src/utility/ReaderUint8Array.ts +187 -0
  309. package/src/utility/ScriptTemplateBRC29.ts +73 -0
  310. package/src/utility/__tests/utilityHelpers.noBuffer.test.ts +109 -0
  311. package/src/utility/aggregateResults.ts +68 -0
  312. package/src/utility/identityUtils.ts +159 -0
  313. package/src/utility/index.all.ts +7 -0
  314. package/src/utility/index.client.ts +7 -0
  315. package/src/utility/parseTxScriptOffsets.ts +29 -0
  316. package/src/utility/stampLog.ts +69 -0
  317. package/src/utility/tscProofToMerklePath.ts +48 -0
  318. package/src/utility/utilityHelpers.buffer.ts +34 -0
  319. package/src/utility/utilityHelpers.noBuffer.ts +60 -0
  320. package/src/utility/utilityHelpers.ts +275 -0
  321. package/src/wab-client/WABClient.ts +94 -0
  322. package/src/wab-client/__tests/WABClient.man.test.ts +59 -0
  323. package/src/wab-client/auth-method-interactors/AuthMethodInteractor.ts +47 -0
  324. package/src/wab-client/auth-method-interactors/DevConsoleInteractor.ts +73 -0
  325. package/src/wab-client/auth-method-interactors/PersonaIDInteractor.ts +35 -0
  326. package/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.ts +72 -0
  327. package/syncVersions.js +71 -0
  328. package/test/Wallet/StorageClient/storageClient.man.test.ts +75 -0
  329. package/test/Wallet/action/abortAction.test.ts +47 -0
  330. package/test/Wallet/action/createAction.test.ts +299 -0
  331. package/test/Wallet/action/createAction2.test.ts +1273 -0
  332. package/test/Wallet/action/createActionToGenerateBeefs.man.test.ts +293 -0
  333. package/test/Wallet/action/internalizeAction.a.test.ts +286 -0
  334. package/test/Wallet/action/internalizeAction.test.ts +682 -0
  335. package/test/Wallet/action/relinquishOutput.test.ts +37 -0
  336. package/test/Wallet/certificate/acquireCertificate.test.ts +298 -0
  337. package/test/Wallet/certificate/listCertificates.test.ts +346 -0
  338. package/test/Wallet/construct/Wallet.constructor.test.ts +57 -0
  339. package/test/Wallet/get/getHeaderForHeight.test.ts +82 -0
  340. package/test/Wallet/get/getHeight.test.ts +52 -0
  341. package/test/Wallet/get/getKnownTxids.test.ts +86 -0
  342. package/test/Wallet/get/getNetwork.test.ts +27 -0
  343. package/test/Wallet/get/getVersion.test.ts +27 -0
  344. package/test/Wallet/list/listActions.test.ts +279 -0
  345. package/test/Wallet/list/listActions2.test.ts +1381 -0
  346. package/test/Wallet/list/listCertificates.test.ts +118 -0
  347. package/test/Wallet/list/listOutputs.test.ts +447 -0
  348. package/test/Wallet/live/walletLive.man.test.ts +521 -0
  349. package/test/Wallet/local/localWallet.man.test.ts +93 -0
  350. package/test/Wallet/local/localWallet2.man.test.ts +277 -0
  351. package/test/Wallet/signAction/mountaintop.man.test.ts +130 -0
  352. package/test/Wallet/specOps/specOps.man.test.ts +220 -0
  353. package/test/Wallet/support/janitor.man.test.ts +40 -0
  354. package/test/Wallet/support/operations.man.test.ts +407 -0
  355. package/test/Wallet/support/reqErrorReview.2025.05.06.man.test.ts +347 -0
  356. package/test/Wallet/sync/Wallet.sync.test.ts +215 -0
  357. package/test/Wallet/sync/Wallet.updateWalletLegacyTestData.man.test.ts +203 -0
  358. package/test/Wallet/sync/setActive.test.ts +170 -0
  359. package/test/WalletClient/LocalKVStore.man.test.ts +114 -0
  360. package/test/WalletClient/WERR.man.test.ts +35 -0
  361. package/test/bsv-ts-sdk/LocalKVStore.test.ts +102 -0
  362. package/test/checkDB.ts +57 -0
  363. package/test/checkdb +0 -0
  364. package/test/examples/backup.man.test.ts +59 -0
  365. package/test/examples/pushdrop.test.ts +282 -0
  366. package/test/monitor/Monitor.test.ts +620 -0
  367. package/test/services/Services.test.ts +263 -0
  368. package/test/storage/KnexMigrations.test.ts +86 -0
  369. package/test/storage/StorageMySQLDojoReader.man.test.ts +60 -0
  370. package/test/storage/count.test.ts +177 -0
  371. package/test/storage/find.test.ts +195 -0
  372. package/test/storage/findLegacy.test.ts +67 -0
  373. package/test/storage/idb/allocateChange.test.ts +251 -0
  374. package/test/storage/idb/count.test.ts +158 -0
  375. package/test/storage/idb/find.test.ts +177 -0
  376. package/test/storage/idb/idbSpeed.test.ts +36 -0
  377. package/test/storage/idb/insert.test.ts +268 -0
  378. package/test/storage/idb/transactionAbort.test.ts +108 -0
  379. package/test/storage/idb/update.test.ts +999 -0
  380. package/test/storage/insert.test.ts +278 -0
  381. package/test/storage/update.test.ts +1021 -0
  382. package/test/storage/update2.test.ts +897 -0
  383. package/test/utils/TestUtilsWalletStorage.ts +2526 -0
  384. package/test/utils/localWalletMethods.ts +363 -0
  385. package/test/utils/removeFailedFromDatabase.sql +17 -0
  386. package/ts2md.json +44 -0
  387. package/tsconfig.all.json +31 -0
  388. package/tsconfig.client.json +29 -0
  389. package/tsconfig.json +17 -0
  390. package/tsconfig.mobile.json +28 -0
@@ -0,0 +1,947 @@
1
+ import {
2
+ Beef,
3
+ OriginatorDomainNameStringUnder250Bytes,
4
+ Random,
5
+ ReviewActionResult,
6
+ Script,
7
+ Utils,
8
+ Validation
9
+ } from '@bsv/sdk'
10
+ import {
11
+ generateChangeSdk,
12
+ GenerateChangeSdkChangeInput,
13
+ GenerateChangeSdkParams,
14
+ maxPossibleSatoshis
15
+ } from './generateChange'
16
+ import { StorageProvider, validateStorageFeeModel } from '../StorageProvider'
17
+ import {
18
+ AuthId,
19
+ StorageCreateActionResult,
20
+ StorageCreateTransactionSdkInput,
21
+ StorageCreateTransactionSdkOutput,
22
+ StorageFeeModel,
23
+ StorageGetBeefOptions,
24
+ StorageProvidedBy
25
+ } from '../../sdk/WalletStorage.interfaces'
26
+ import { WERR_INTERNAL, WERR_INVALID_PARAMETER, WERR_REVIEW_ACTIONS } from '../../sdk/WERR_errors'
27
+ import {
28
+ randomBytesBase64,
29
+ verifyId,
30
+ verifyInteger,
31
+ verifyNumber,
32
+ verifyOne,
33
+ verifyOneOrNone,
34
+ verifyTruthy
35
+ } from '../../utility/utilityHelpers'
36
+ import { TableOutputBasket } from '../schema/tables/TableOutputBasket'
37
+ import { TableOutput } from '../schema/tables/TableOutput'
38
+ import { asArray, asString } from '../../utility/utilityHelpers.noBuffer'
39
+ import { TableOutputTag } from '../schema/tables/TableOutputTag'
40
+ import { TableTransaction } from '../schema/tables/TableTransaction'
41
+ import { EntityProvenTx } from '../schema/entities/EntityProvenTx'
42
+ import { throwDummyReviewActions } from '../../Wallet'
43
+ import { createStorageServiceChargeScript } from './offsetKey'
44
+
45
+ let disableDoubleSpendCheckForTest = true
46
+ export function setDisableDoubleSpendCheckForTest(v: boolean) {
47
+ disableDoubleSpendCheckForTest = v
48
+ }
49
+
50
+ export async function createAction(
51
+ storage: StorageProvider,
52
+ auth: AuthId,
53
+ vargs: Validation.ValidCreateActionArgs,
54
+ originator?: OriginatorDomainNameStringUnder250Bytes
55
+ ): Promise<StorageCreateActionResult> {
56
+ const logger = vargs.logger
57
+ logger?.group(`storage createAction`)
58
+ //stampLog(vargs, `start storage createTransactionSdk`)
59
+
60
+ if (vargs.isTestWerrReviewActions) throwDummyReviewActions()
61
+
62
+ if (!vargs.isNewTx)
63
+ // The purpose of this function is to create the initial storage records associated
64
+ // with a new transaction. It's an error if we have no new inputs or outputs...
65
+ throw new WERR_INTERNAL()
66
+
67
+ /**
68
+ * Steps to create a transaction:
69
+ * - Verify that all inputs either have proof in vargs.inputBEEF or that options.trustSelf === 'known' and input txid.vout are known valid to storage.
70
+ * - Create a new transaction record with status 'unsigned' as the anchor for construction work and to new outputs.
71
+ * - Create all transaction labels.
72
+ * - Add new commission output
73
+ * - Attempt to fund the transaction by allocating change outputs:
74
+ * - As each change output is selected it is simultaneously locked.
75
+ * - Create all new output, basket, tag records
76
+ * - If requested, create result Beef with complete proofs for all inputs used
77
+ * - Create result inputs with source locking scripts
78
+ * - Create result outputs with new locking scripts.
79
+ * - Create and return result.
80
+ */
81
+
82
+ const userId = auth.userId!
83
+ const { storageBeef, beef, xinputs } = await validateRequiredInputs(storage, userId, vargs)
84
+ logger?.log('validated required inputs')
85
+ const xoutputs = validateRequiredOutputs(storage, userId, vargs)
86
+ logger?.log('validated required outputs')
87
+
88
+ const changeBasketName = 'default'
89
+ const changeBasket = verifyOne(
90
+ await storage.findOutputBaskets({
91
+ partial: { userId, name: changeBasketName }
92
+ }),
93
+ `Invalid outputGeneration basket "${changeBasketName}"`
94
+ )
95
+ logger?.log('found change basket')
96
+
97
+ const noSendChangeIn = await validateNoSendChange(storage, userId, vargs, changeBasket)
98
+ logger?.log('validated noSendChange')
99
+
100
+ const availableChangeCount = await storage.countChangeInputs(userId, changeBasket.basketId, !vargs.isDelayed)
101
+ logger?.log(`counted change inputs ${availableChangeCount}`)
102
+
103
+ const feeModel = validateStorageFeeModel(storage.feeModel)
104
+ logger?.log(`validated fee model ${JSON.stringify(feeModel)}`)
105
+
106
+ const newTx = await createNewTxRecord(storage, userId, vargs, storageBeef)
107
+ logger?.log(`created new transaction record`)
108
+
109
+ const ctx: CreateTransactionSdkContext = {
110
+ xinputs,
111
+ xoutputs,
112
+ changeBasket,
113
+ noSendChangeIn,
114
+ availableChangeCount,
115
+ feeModel,
116
+ transactionId: newTx.transactionId!
117
+ }
118
+
119
+ const { allocatedChange, changeOutputs, derivationPrefix, maxPossibleSatoshisAdjustment } =
120
+ await fundNewTransactionSdk(storage, userId, vargs, ctx)
121
+ logger?.log(`funded new transaction`)
122
+
123
+ if (maxPossibleSatoshisAdjustment) {
124
+ const a = maxPossibleSatoshisAdjustment
125
+ if (ctx.xoutputs[a.fixedOutputIndex].satoshis !== maxPossibleSatoshis) throw new WERR_INTERNAL()
126
+ ctx.xoutputs[a.fixedOutputIndex].satoshis = a.satoshis
127
+ logger?.log(`adjusted change outputs to max possible`)
128
+ }
129
+
130
+ // The satoshis of the transaction is the satoshis we get back in change minus the satoshis we spend.
131
+ const satoshis =
132
+ changeOutputs.reduce((a, e) => a + e.satoshis!, 0) - allocatedChange.reduce((a, e) => a + e.satoshis!, 0)
133
+ await storage.updateTransaction(newTx.transactionId!, { satoshis })
134
+
135
+ const { outputs, changeVouts } = await createNewOutputs(storage, userId, vargs, ctx, changeOutputs)
136
+ logger?.log(`created new output records`)
137
+
138
+ const inputBeef = await mergeAllocatedChangeBeefs(storage, userId, vargs, allocatedChange, beef)
139
+ logger?.log(`merged allocated change beefs`)
140
+
141
+ const inputs = await createNewInputs(storage, userId, vargs, ctx, allocatedChange)
142
+ logger?.log(`created new inputs`)
143
+
144
+ const r: StorageCreateActionResult = {
145
+ reference: newTx.reference!,
146
+ version: newTx.version!,
147
+ lockTime: newTx.lockTime!,
148
+ inputs,
149
+ outputs,
150
+ derivationPrefix,
151
+ inputBeef,
152
+ noSendChangeOutputVouts: vargs.isNoSend ? changeVouts : undefined
153
+ }
154
+
155
+ logger?.groupEnd()
156
+ return r
157
+ }
158
+
159
+ interface CreateTransactionSdkContext {
160
+ xinputs: XValidCreateActionInput[]
161
+ xoutputs: XValidCreateActionOutput[]
162
+ changeBasket: TableOutputBasket
163
+ noSendChangeIn: TableOutput[]
164
+ availableChangeCount: number
165
+ feeModel: StorageFeeModel
166
+ transactionId: number
167
+ }
168
+
169
+ interface XValidCreateActionInput extends Validation.ValidCreateActionInput {
170
+ vin: number
171
+ lockingScript: Script
172
+ satoshis: number
173
+ output?: TableOutput
174
+ }
175
+
176
+ export interface XValidCreateActionOutput extends Validation.ValidCreateActionOutput {
177
+ vout: number
178
+ providedBy: StorageProvidedBy
179
+ purpose?: string
180
+ derivationSuffix?: string
181
+ keyOffset?: string
182
+ }
183
+
184
+ function makeDefaultOutput(userId: number, transactionId: number, satoshis: number, vout: number): TableOutput {
185
+ const now = new Date()
186
+ const output: TableOutput = {
187
+ created_at: now,
188
+ updated_at: now,
189
+ outputId: 0,
190
+ userId,
191
+ transactionId,
192
+ satoshis: satoshis,
193
+ vout,
194
+
195
+ basketId: undefined,
196
+ change: false,
197
+ customInstructions: undefined,
198
+ derivationPrefix: undefined,
199
+ derivationSuffix: undefined,
200
+ outputDescription: '',
201
+ lockingScript: undefined,
202
+ providedBy: 'you',
203
+ purpose: '',
204
+ senderIdentityKey: undefined,
205
+ spendable: true,
206
+ spendingDescription: undefined,
207
+ spentBy: undefined,
208
+ txid: undefined,
209
+ type: ''
210
+ }
211
+ return output
212
+ }
213
+
214
+ async function createNewInputs(
215
+ storage: StorageProvider,
216
+ userId: number,
217
+ vargs: Validation.ValidCreateActionArgs,
218
+ ctx: CreateTransactionSdkContext,
219
+ allocatedChange: TableOutput[]
220
+ ): Promise<StorageCreateTransactionSdkInput[]> {
221
+ const r: StorageCreateTransactionSdkInput[] = []
222
+
223
+ const newInputs: {
224
+ i?: XValidCreateActionInput
225
+ o?: TableOutput
226
+ unlockLen?: number
227
+ }[] = []
228
+ for (const i of ctx.xinputs) {
229
+ const o = i.output
230
+ newInputs.push({ i, o })
231
+ if (o) {
232
+ await storage.transaction(async trx => {
233
+ const o2 = verifyOne(await storage.findOutputs({ partial: { outputId: o.outputId }, trx }))
234
+ if (o2.spentBy !== undefined) {
235
+ const spendingTx = await storage.findTransactionById(verifyId(o2.spentBy), trx)
236
+ if (spendingTx && spendingTx.txid) {
237
+ const beef = await storage.getBeefForTransaction(spendingTx.txid, {})
238
+ const rar: ReviewActionResult = {
239
+ txid: '',
240
+ status: 'doubleSpend',
241
+ competingTxs: [spendingTx.txid!],
242
+ competingBeef: beef.toBinary()
243
+ }
244
+ throw new WERR_REVIEW_ACTIONS([rar], [])
245
+ }
246
+ }
247
+ if (o2.spendable != true) {
248
+ throw new WERR_INVALID_PARAMETER(
249
+ `inputs[${i.vin}]`,
250
+ `spendable output. output ${o.txid}:${o.vout} appears to have been spent (spendable=${o2.spendable}).`
251
+ )
252
+ }
253
+ await storage.updateOutput(
254
+ o.outputId!,
255
+ {
256
+ spendable: false,
257
+ spentBy: ctx.transactionId,
258
+ spendingDescription: i.inputDescription
259
+ },
260
+ trx
261
+ )
262
+ })
263
+ }
264
+ }
265
+
266
+ for (const o of allocatedChange) {
267
+ newInputs.push({ o, unlockLen: 107 })
268
+ }
269
+
270
+ let vin = -1
271
+ for (const { i, o, unlockLen } of newInputs) {
272
+ vin++
273
+ if (o) {
274
+ if (!i && !unlockLen) throw new WERR_INTERNAL(`vin ${vin} non-fixedInput without unlockLen`)
275
+ const sourceTransaction =
276
+ vargs.includeAllSourceTransactions && vargs.isSignAction
277
+ ? await storage.getRawTxOfKnownValidTransaction(o.txid!)
278
+ : undefined
279
+ const ri: StorageCreateTransactionSdkInput = {
280
+ vin,
281
+ sourceTxid: o.txid!,
282
+ sourceVout: o!.vout!,
283
+ sourceSatoshis: o.satoshis!,
284
+ sourceLockingScript: asString(o.lockingScript!),
285
+ sourceTransaction,
286
+ unlockingScriptLength: unlockLen ? unlockLen : i!.unlockingScriptLength,
287
+ providedBy: i && o.providedBy === 'storage' ? 'you-and-storage' : (o.providedBy! as StorageProvidedBy),
288
+ type: o.type,
289
+ spendingDescription: o.spendingDescription || undefined,
290
+ derivationPrefix: o.derivationPrefix || undefined,
291
+ derivationSuffix: o.derivationSuffix || undefined,
292
+ senderIdentityKey: o.senderIdentityKey || undefined
293
+ }
294
+ r.push(ri)
295
+ } else {
296
+ if (!i) throw new WERR_INTERNAL(`vin ${vin} without output or xinput`)
297
+ // user specified input with no corresponding output being spent.
298
+ const ri: StorageCreateTransactionSdkInput = {
299
+ vin,
300
+ sourceTxid: i.outpoint.txid,
301
+ sourceVout: i.outpoint.vout,
302
+ sourceSatoshis: i.satoshis,
303
+ sourceLockingScript: i.lockingScript.toHex(),
304
+ unlockingScriptLength: i.unlockingScriptLength,
305
+ providedBy: 'you',
306
+ type: 'custom',
307
+ spendingDescription: undefined,
308
+ derivationPrefix: undefined,
309
+ derivationSuffix: undefined,
310
+ senderIdentityKey: undefined
311
+ }
312
+ r.push(ri)
313
+ }
314
+ }
315
+ return r
316
+ }
317
+
318
+ async function createNewOutputs(
319
+ storage: StorageProvider,
320
+ userId: number,
321
+ vargs: Validation.ValidCreateActionArgs,
322
+ ctx: CreateTransactionSdkContext,
323
+ changeOutputs: TableOutput[]
324
+ ): Promise<{
325
+ outputs: StorageCreateTransactionSdkOutput[]
326
+ changeVouts: number[]
327
+ }> {
328
+ const outputs: StorageCreateTransactionSdkOutput[] = []
329
+
330
+ // Lookup output baskets
331
+ const txBaskets: Record<string, TableOutputBasket> = {}
332
+ for (const xo of ctx.xoutputs) {
333
+ if (xo.basket !== undefined && !txBaskets[xo.basket])
334
+ txBaskets[xo.basket] = await storage.findOrInsertOutputBasket(userId, xo.basket!)
335
+ }
336
+ // Lookup output tags
337
+ const txTags: Record<string, TableOutputTag> = {}
338
+ for (const xo of ctx.xoutputs) {
339
+ for (const tag of xo.tags) {
340
+ txTags[tag] = await storage.findOrInsertOutputTag(userId, tag)
341
+ }
342
+ }
343
+
344
+ const newOutputs: { o: TableOutput; tags: string[] }[] = []
345
+
346
+ for (const xo of ctx.xoutputs) {
347
+ const lockingScript = asArray(xo.lockingScript)
348
+ if (xo.purpose === 'service-charge') {
349
+ const now = new Date()
350
+ await storage.insertCommission({
351
+ userId,
352
+ transactionId: ctx.transactionId,
353
+ lockingScript,
354
+ satoshis: xo.satoshis,
355
+ isRedeemed: false,
356
+ keyOffset: verifyTruthy(xo.keyOffset),
357
+ created_at: now,
358
+ updated_at: now,
359
+ commissionId: 0
360
+ })
361
+
362
+ const o = makeDefaultOutput(userId, ctx.transactionId, xo.satoshis, xo.vout)
363
+ o.lockingScript = lockingScript
364
+ o.providedBy = 'storage'
365
+ o.purpose = 'storage-commission'
366
+ o.type = 'custom'
367
+ o.spendable = false
368
+
369
+ newOutputs.push({ o, tags: [] })
370
+ } else {
371
+ // The user wants tracking if they put their output in a basket
372
+ const basketId = !xo.basket ? undefined : txBaskets[xo.basket].basketId!
373
+
374
+ const o = makeDefaultOutput(userId, ctx.transactionId, xo.satoshis, xo.vout)
375
+ o.lockingScript = lockingScript
376
+ o.basketId = basketId
377
+ o.customInstructions = xo.customInstructions
378
+ o.outputDescription = xo.outputDescription
379
+ o.providedBy = xo.providedBy
380
+ o.purpose = xo.purpose || ''
381
+ o.type = 'custom'
382
+
383
+ newOutputs.push({ o, tags: xo.tags })
384
+ }
385
+ }
386
+
387
+ for (const o of changeOutputs) {
388
+ o.spendable = true
389
+ newOutputs.push({ o, tags: [] })
390
+ }
391
+
392
+ if (vargs.options.randomizeOutputs) {
393
+ const randomVals: number[] = []
394
+
395
+ const nextRandomVal = (): number => {
396
+ let val = 0
397
+ if (!randomVals || randomVals.length === 0) {
398
+ val = Math.random()
399
+ } else {
400
+ val = randomVals.shift() || 0
401
+ randomVals.push(val)
402
+ }
403
+ return val
404
+ }
405
+
406
+ /** In-place array shuffle */
407
+ const shuffleArray = <T>(array: T[]): T[] => {
408
+ let currentIndex = array.length
409
+ let temporaryValue: T
410
+ let randomIndex: number
411
+ while (currentIndex !== 0) {
412
+ randomIndex = Math.floor(nextRandomVal() * currentIndex)
413
+ currentIndex -= 1
414
+ temporaryValue = array[currentIndex]
415
+ array[currentIndex] = array[randomIndex]
416
+ array[randomIndex] = temporaryValue
417
+ }
418
+ return array
419
+ }
420
+
421
+ let vout = -1
422
+ const newVouts = Array<number>(newOutputs.length)
423
+ for (let i = 0; i < newVouts.length; i++) newVouts[i] = i
424
+ shuffleArray(newVouts)
425
+ for (const no of newOutputs) {
426
+ vout++
427
+ if (no.o.vout !== vout) throw new WERR_INTERNAL(`new output ${vout} has out of order vout ${no.o.vout}`)
428
+ no.o.vout = newVouts[vout]
429
+ }
430
+ }
431
+
432
+ const changeVouts: number[] = []
433
+ for (const { o, tags } of newOutputs) {
434
+ o.outputId = await storage.insertOutput(o)
435
+
436
+ if (o.change && o.purpose === 'change' && o.providedBy === 'storage') changeVouts.push(o.vout!)
437
+
438
+ // Add tags to the output
439
+ for (const tagName of tags) {
440
+ const tag = txTags[tagName]!
441
+ await storage.findOrInsertOutputTagMap(verifyId(o.outputId), verifyId(tag.outputTagId))
442
+ }
443
+
444
+ const ro: StorageCreateTransactionSdkOutput = {
445
+ vout: verifyInteger(o.vout),
446
+ satoshis: Validation.validateSatoshis(o.satoshis, 'o.satoshis'),
447
+ lockingScript: !o.lockingScript ? '' : asString(o.lockingScript),
448
+ providedBy: verifyTruthy(o.providedBy) as StorageProvidedBy,
449
+ purpose: o.purpose || undefined,
450
+ basket: Object.values(txBaskets).find(b => b.basketId === o.basketId)?.name,
451
+ tags: tags,
452
+ outputDescription: o.outputDescription,
453
+ derivationSuffix: o.derivationSuffix,
454
+ customInstructions: o.customInstructions
455
+ }
456
+ outputs.push(ro)
457
+ }
458
+
459
+ return { outputs, changeVouts }
460
+ }
461
+
462
+ async function createNewTxRecord(
463
+ storage: StorageProvider,
464
+ userId: number,
465
+ vargs: Validation.ValidCreateActionArgs,
466
+ storageBeef: Beef
467
+ ): Promise<TableTransaction> {
468
+ const now = new Date()
469
+ const newTx: TableTransaction = {
470
+ created_at: now,
471
+ updated_at: now,
472
+ transactionId: 0,
473
+ version: vargs.version,
474
+ lockTime: vargs.lockTime,
475
+ status: 'unsigned',
476
+ reference: randomBytesBase64(12),
477
+ satoshis: 0, // updated after fundingTransaction
478
+ userId,
479
+ isOutgoing: true,
480
+ inputBEEF: storageBeef.toBinary(),
481
+ description: vargs.description,
482
+ txid: undefined,
483
+ rawTx: undefined
484
+ }
485
+ newTx.transactionId = await storage.insertTransaction(newTx)
486
+
487
+ for (const label of vargs.labels) {
488
+ const txLabel = await storage.findOrInsertTxLabel(userId, label)
489
+ await storage.findOrInsertTxLabelMap(verifyId(newTx.transactionId), verifyId(txLabel.txLabelId))
490
+ }
491
+
492
+ return newTx
493
+ }
494
+
495
+ /**
496
+ * Convert vargs.outputs:
497
+ *
498
+ * lockingScript: HexString
499
+ * satoshis: SatoshiValue
500
+ * outputDescription: DescriptionString5to50Bytes
501
+ * basket?: BasketStringUnder300Bytes
502
+ * customInstructions?: string
503
+ * tags: BasketStringUnderBytes[]
504
+ *
505
+ * to XValidCreateActionOutput (which aims for StorageCreateTransactionSdkOutput)
506
+ *
507
+ * adds:
508
+ * vout: number
509
+ * providedBy: StorageProvidedBy
510
+ * purpose?: string
511
+ * derivationSuffix?: string
512
+ * keyOffset?: string
513
+ *
514
+ * @param vargs
515
+ * @returns xoutputs
516
+ */
517
+ function validateRequiredOutputs(
518
+ storage: StorageProvider,
519
+ userId: number,
520
+ vargs: Validation.ValidCreateActionArgs
521
+ ): XValidCreateActionOutput[] {
522
+ const xoutputs: XValidCreateActionOutput[] = []
523
+ let vout = -1
524
+ for (const output of vargs.outputs) {
525
+ vout++
526
+ const xo: XValidCreateActionOutput = {
527
+ ...output,
528
+ vout,
529
+ providedBy: 'you',
530
+ purpose: undefined,
531
+ derivationSuffix: undefined,
532
+ keyOffset: undefined
533
+ }
534
+ xoutputs.push(xo)
535
+ }
536
+
537
+ if (storage.commissionSatoshis > 0 && storage.commissionPubKeyHex) {
538
+ vout++
539
+ const { script, keyOffset } = createStorageServiceChargeScript(storage.commissionPubKeyHex)
540
+ xoutputs.push({
541
+ lockingScript: script,
542
+ satoshis: storage.commissionSatoshis,
543
+ outputDescription: 'Storage Service Charge',
544
+ basket: undefined,
545
+ tags: [],
546
+
547
+ vout,
548
+ providedBy: 'storage',
549
+ purpose: 'service-charge',
550
+ keyOffset
551
+ })
552
+ }
553
+
554
+ return xoutputs
555
+ }
556
+
557
+ /**
558
+ * Verify that we are in posession of validity proof data for any inputs being proposed for a new transaction.
559
+ *
560
+ * `vargs.inputs` is the source of inputs.
561
+ * `vargs.inputBEEF` may include new user supplied validity data.
562
+ * 'vargs.options.trustSelf === 'known'` indicates whether we can rely on the storage database records.
563
+ *
564
+ * If there are no inputs, returns an empty `Beef`.
565
+ *
566
+ * Always pulls rawTx data into first level of validity chains so that parsed transaction data is available
567
+ * and checks input sourceSatoshis as well as filling in input sourceLockingScript.
568
+ *
569
+ * This data may be pruned again before being returned to the user based on `vargs.options.knownTxids`.
570
+ *
571
+ * @param storage
572
+ * @param userId
573
+ * @param vargs
574
+ * @returns {storageBeef} containing only validity proof data for only unknown required inputs.
575
+ * @returns {beef} containing verified validity proof data for all required inputs.
576
+ * @returns {xinputs} extended validated required inputs.
577
+ */
578
+ async function validateRequiredInputs(
579
+ storage: StorageProvider,
580
+ userId: number,
581
+ vargs: Validation.ValidCreateActionArgs
582
+ ): Promise<{
583
+ storageBeef: Beef
584
+ beef: Beef
585
+ xinputs: XValidCreateActionInput[]
586
+ }> {
587
+ //stampLog(vargs, `start storage verifyInputBeef`)
588
+
589
+ const beef = new Beef()
590
+
591
+ if (vargs.inputs.length === 0) return { storageBeef: beef, beef, xinputs: [] }
592
+
593
+ if (vargs.inputBEEF) beef.mergeBeef(vargs.inputBEEF)
594
+
595
+ const xinputs: XValidCreateActionInput[] = vargs.inputs.map((input, vin) => ({
596
+ ...input,
597
+ vin,
598
+ satoshis: -1,
599
+ lockingScript: new Script(),
600
+ output: undefined
601
+ }))
602
+
603
+ const trustSelf = vargs.options.trustSelf === 'known'
604
+
605
+ const inputTxids: Record<string, boolean> = {}
606
+ for (const input of xinputs) inputTxids[input.outpoint.txid] = true
607
+
608
+ // Check beef from user that either there are no txidOnly entries,
609
+ // or that we can trust storage data and it does indeed vouch
610
+ // for any txidOnly entries
611
+ for (const btx of beef.txs) {
612
+ if (btx.isTxidOnly) {
613
+ if (!trustSelf)
614
+ throw new WERR_INVALID_PARAMETER('inputBEEF', `valid and contain complete proof data for ${btx.txid}`)
615
+ if (!inputTxids[btx.txid]) {
616
+ // inputTxids are checked next
617
+ const isKnown = await storage.verifyKnownValidTransaction(btx.txid)
618
+ if (!isKnown)
619
+ throw new WERR_INVALID_PARAMETER('inputBEEF', `valid and contain complete proof data for unknown ${btx.txid}`)
620
+ }
621
+ }
622
+ }
623
+
624
+ // Make sure that there's an entry for all inputs txid values:
625
+ for (const txid of Object.keys(inputTxids)) {
626
+ let btx = beef.findTxid(txid)
627
+ if (!btx && trustSelf) {
628
+ if (await storage.verifyKnownValidTransaction(txid)) btx = beef.mergeTxidOnly(txid)
629
+ }
630
+ if (!btx) throw new WERR_INVALID_PARAMETER('inputBEEF', `valid and contain proof data for possibly known ${txid}`)
631
+ }
632
+
633
+ if (!(await beef.verify(await storage.getServices().getChainTracker(), true))) {
634
+ console.log(`verifyInputBeef failed, inputBEEF failed to verify.\n${beef.toLogString()}\n`)
635
+ //console.log(`verifyInputBeef failed, inputBEEF failed to verify.\n${stampLogFormat(vargs.log)}\n${beef.toLogString()}\n`)
636
+ throw new WERR_INVALID_PARAMETER('inputBEEF', 'valid Beef when factoring options.trustSelf')
637
+ }
638
+
639
+ // beef may now be trusted and has a BeefTx for every input txid.
640
+
641
+ const storageBeef = beef.clone()
642
+
643
+ for (const input of xinputs) {
644
+ const { txid, vout } = input.outpoint
645
+ const output = verifyOneOrNone(await storage.findOutputs({ partial: { userId, txid, vout } }))
646
+ if (output) {
647
+ if (output.change) {
648
+ throw new WERR_INVALID_PARAMETER(
649
+ `inputs[${input.vin}]`,
650
+ 'an unmanaged input. Change outputs are managed by your wallet.'
651
+ )
652
+ }
653
+ input.output = output
654
+ if (!Array.isArray(output.lockingScript) || !Number.isInteger(output.satoshis))
655
+ throw new WERR_INVALID_PARAMETER(`${txid}.${vout}`, 'output with valid lockingScript and satoshis')
656
+ if (!disableDoubleSpendCheckForTest && !output.spendable && !vargs.isNoSend)
657
+ throw new WERR_INVALID_PARAMETER(`${txid}.${vout}`, 'spendable output unless noSend is true')
658
+ // input is spending an existing user output which has an lockingScript
659
+ input.satoshis = Validation.validateSatoshis(output.satoshis, 'output.satoshis')
660
+ input.lockingScript = Script.fromBinary(asArray(output.lockingScript!))
661
+ } else {
662
+ let btx = beef.findTxid(txid)!
663
+ if (btx.isTxidOnly) {
664
+ const { rawTx, proven } = await storage.getProvenOrRawTx(txid)
665
+ //stampLog(vargs, `... storage verifyInputBeef getProvenOrRawTx ${txid} ${proven ? 'proven' : rawTx ? 'rawTx' : 'unknown'}`)
666
+ if (!rawTx) throw new WERR_INVALID_PARAMETER('inputBEEF', `valid and contain proof data for ${txid}`)
667
+ btx = beef.mergeRawTx(asArray(rawTx))
668
+ if (proven) beef.mergeBump(new EntityProvenTx(proven).getMerklePath())
669
+ }
670
+ // btx is valid has parsed transaction data.
671
+ if (vout >= btx.tx!.outputs.length) throw new WERR_INVALID_PARAMETER(`${txid}.${vout}`, 'valid outpoint')
672
+ const so = btx.tx!.outputs[vout]
673
+ input.satoshis = Validation.validateSatoshis(so.satoshis, 'so.satoshis')
674
+ input.lockingScript = so.lockingScript
675
+ }
676
+ }
677
+
678
+ return { beef, storageBeef, xinputs }
679
+ }
680
+
681
+ async function verifyBeefFixOrhpans(beef: Beef, storage: StorageProvider): Promise<boolean> {
682
+ const r = beef.verifyValid()
683
+ if (!r.valid) {
684
+ // Beef is structurally invalid.
685
+ return false
686
+ }
687
+ const heights = Object.keys(r.roots)
688
+ const services = storage.getServices()
689
+ const chainTracker = await services.getChainTracker()
690
+ let rootsAreValid = true
691
+ for (const height of heights) {
692
+ const isValid = await chainTracker.isValidRootForHeight(r.roots[height], Number(height))
693
+ if (isValid) continue
694
+ // The original block may have been orphaned, check for a new proof.
695
+ const mp = beef.bumps.find(b => b.blockHeight === Number(height))
696
+ //const p = await services.getMerklePath()
697
+ }
698
+ return false
699
+ }
700
+
701
+ async function validateNoSendChange(
702
+ storage: StorageProvider,
703
+ userId: number,
704
+ vargs: Validation.ValidCreateActionArgs,
705
+ changeBasket: TableOutputBasket
706
+ ): Promise<TableOutput[]> {
707
+ const r: TableOutput[] = []
708
+
709
+ if (!vargs.isNoSend) return []
710
+
711
+ const noSendChange = vargs.options.noSendChange
712
+
713
+ if (noSendChange && noSendChange.length > 0) {
714
+ for (const op of noSendChange) {
715
+ const output = verifyOneOrNone(
716
+ await storage.findOutputs({
717
+ partial: { userId, txid: op.txid, vout: op.vout }
718
+ })
719
+ )
720
+ // noSendChange is not marked spendable until sent, may not already be spent, and must have a valid greater than zero satoshis
721
+ if (
722
+ !output ||
723
+ output.providedBy !== 'storage' ||
724
+ output.purpose !== 'change' ||
725
+ output.spendable === false ||
726
+ Number.isInteger(output.spentBy) ||
727
+ !verifyNumber(output.satoshis) ||
728
+ output.basketId !== changeBasket.basketId
729
+ )
730
+ throw new WERR_INVALID_PARAMETER('noSendChange outpoint', 'valid')
731
+ if (-1 < r.findIndex(o => o.outputId === output.outputId))
732
+ // noSendChange duplicate OutPoints are not allowed.
733
+ throw new WERR_INVALID_PARAMETER('noSendChange outpoint', 'unique. Duplicates are not allowed.')
734
+ r.push(output)
735
+ }
736
+ }
737
+
738
+ return r
739
+ }
740
+
741
+ async function fundNewTransactionSdk(
742
+ storage: StorageProvider,
743
+ userId: number,
744
+ vargs: Validation.ValidCreateActionArgs,
745
+ ctx: CreateTransactionSdkContext
746
+ ): Promise<{
747
+ allocatedChange: TableOutput[]
748
+ changeOutputs: TableOutput[]
749
+ derivationPrefix: string
750
+ maxPossibleSatoshisAdjustment?: {
751
+ fixedOutputIndex: number
752
+ satoshis: number
753
+ }
754
+ }> {
755
+ const params: GenerateChangeSdkParams = {
756
+ fixedInputs: ctx.xinputs.map(xi => ({
757
+ satoshis: xi.satoshis,
758
+ unlockingScriptLength: xi.unlockingScriptLength!
759
+ })),
760
+ fixedOutputs: ctx.xoutputs.map(xo => ({
761
+ satoshis: xo.satoshis,
762
+ lockingScriptLength: xo.lockingScript.length / 2
763
+ })),
764
+ feeModel: ctx.feeModel,
765
+ changeInitialSatoshis: ctx.changeBasket.minimumDesiredUTXOValue,
766
+ changeFirstSatoshis: Math.max(1, Math.round(ctx.changeBasket.minimumDesiredUTXOValue / 4)),
767
+ changeLockingScriptLength: 25,
768
+ changeUnlockingScriptLength: 107,
769
+ targetNetCount: ctx.changeBasket.numberOfDesiredUTXOs - ctx.availableChangeCount,
770
+ randomVals: vargs.randomVals
771
+ }
772
+
773
+ const noSendChange = [...ctx.noSendChangeIn]
774
+ const outputs: Record<number, TableOutput> = {}
775
+
776
+ const allocateChangeInput = async (
777
+ targetSatoshis: number,
778
+ exactSatoshis?: number
779
+ ): Promise<GenerateChangeSdkChangeInput | undefined> => {
780
+ // noSendChange gets allocated first...typically only one input...just allocate in order...
781
+ if (noSendChange.length > 0) {
782
+ const o = noSendChange.pop()!
783
+ outputs[o.outputId!] = o
784
+ // allocate the output in storage, noSendChange is by definition spendable false and part of noSpend transaction batch.
785
+ await storage.updateOutput(o.outputId!, {
786
+ spendable: false,
787
+ spentBy: ctx.transactionId
788
+ })
789
+ o.spendable = false
790
+ o.spentBy = ctx.transactionId
791
+ const r: GenerateChangeSdkChangeInput = {
792
+ outputId: o.outputId!,
793
+ satoshis: o.satoshis!
794
+ }
795
+ return r
796
+ }
797
+
798
+ const basketId = ctx.changeBasket.basketId!
799
+ const o = await storage.allocateChangeInput(
800
+ userId,
801
+ basketId,
802
+ targetSatoshis,
803
+ exactSatoshis,
804
+ !vargs.isDelayed,
805
+ ctx.transactionId
806
+ )
807
+ if (!o) return undefined
808
+ outputs[o.outputId!] = o
809
+ const r: GenerateChangeSdkChangeInput = {
810
+ outputId: o.outputId!,
811
+ satoshis: o.satoshis!
812
+ }
813
+ return r
814
+ }
815
+
816
+ const releaseChangeInput = async (outputId: number): Promise<void> => {
817
+ const nsco = ctx.noSendChangeIn.find(o => o.outputId === outputId)
818
+ if (nsco) {
819
+ noSendChange.push(nsco)
820
+ return
821
+ }
822
+ await storage.updateOutput(outputId, {
823
+ spendable: true,
824
+ spentBy: undefined
825
+ })
826
+ }
827
+
828
+ const gcr = await generateChangeSdk(params, allocateChangeInput, releaseChangeInput, vargs.logger)
829
+
830
+ const nextRandomVal = (): number => {
831
+ let val = 0
832
+ if (!vargs.randomVals || vargs.randomVals.length === 0) {
833
+ val = Math.random()
834
+ } else {
835
+ val = vargs.randomVals.shift() || 0
836
+ vargs.randomVals.push(val)
837
+ }
838
+ return val
839
+ }
840
+
841
+ /**
842
+ * @returns a random integer betweenn min and max, inclussive.
843
+ */
844
+ const rand = (min: number, max: number): number => {
845
+ if (max < min) throw new WERR_INVALID_PARAMETER('max', `less than min (${min}). max is (${max})`)
846
+ return Math.floor(nextRandomVal() * (max - min + 1) + min)
847
+ }
848
+
849
+ const randomDerivation = (count: number): string => {
850
+ let val: number[] = []
851
+ if (!vargs.randomVals || vargs.randomVals.length === 0) {
852
+ val = Random(count)
853
+ } else {
854
+ for (let i = 0; i < count; i++) val.push(rand(0, 255))
855
+ }
856
+ return Utils.toBase64(val)
857
+ }
858
+
859
+ // Generate a derivation prefix for the payment
860
+ const derivationPrefix = randomDerivation(16)
861
+
862
+ const r: {
863
+ allocatedChange: TableOutput[]
864
+ changeOutputs: TableOutput[]
865
+ derivationPrefix: string
866
+ maxPossibleSatoshisAdjustment?: {
867
+ fixedOutputIndex: number
868
+ satoshis: number
869
+ }
870
+ } = {
871
+ maxPossibleSatoshisAdjustment: gcr.maxPossibleSatoshisAdjustment,
872
+ allocatedChange: gcr.allocatedChangeInputs.map(i => outputs[i.outputId]),
873
+ changeOutputs: gcr.changeOutputs.map(
874
+ (o, i) =>
875
+ <TableOutput>{
876
+ // what we knnow now and can insert into the database for this new transaction's change output
877
+ created_at: new Date(),
878
+ updated_at: new Date(),
879
+ outputId: 0,
880
+ userId,
881
+ transactionId: ctx.transactionId,
882
+ vout: params.fixedOutputs.length + i,
883
+ satoshis: o.satoshis,
884
+ basketId: ctx.changeBasket.basketId!,
885
+ spendable: false,
886
+ change: true,
887
+ type: 'P2PKH',
888
+ derivationPrefix,
889
+ derivationSuffix: randomDerivation(16),
890
+ providedBy: 'storage',
891
+ purpose: 'change',
892
+ customInstructions: undefined,
893
+ senderIdentityKey: undefined,
894
+ outputDescription: '',
895
+
896
+ // what will be known when transaction is signed
897
+ txid: undefined,
898
+ lockingScript: undefined,
899
+
900
+ // when this output gets spent
901
+ spentBy: undefined,
902
+ spendingDescription: undefined
903
+ }
904
+ ),
905
+ derivationPrefix
906
+ }
907
+
908
+ return r
909
+ }
910
+
911
+ /**
912
+ * Avoid returning any known raw transaction data by converting any known transaction
913
+ * in the `beef` to txidOnly.
914
+ * @returns undefined if `vargs.options.returnTXIDOnly` or trimmed `Beef`
915
+ */
916
+ function trimInputBeef(beef: Beef, vargs: Validation.ValidCreateActionArgs): number[] | undefined {
917
+ if (vargs.options.returnTXIDOnly) return undefined
918
+ const knownTxids: Record<string, boolean> = {}
919
+ for (const txid of vargs.options.knownTxids) knownTxids[txid] = true
920
+ for (const txid of beef.txs.map(btx => btx.txid)) if (knownTxids[txid]) beef.makeTxidOnly(txid)
921
+ return beef.toBinary()
922
+ }
923
+
924
+ async function mergeAllocatedChangeBeefs(
925
+ storage: StorageProvider,
926
+ userId: number,
927
+ vargs: Validation.ValidCreateActionArgs,
928
+ allocatedChange: TableOutput[],
929
+ beef: Beef
930
+ ): Promise<number[] | undefined> {
931
+ const options: StorageGetBeefOptions = {
932
+ trustSelf: undefined,
933
+ knownTxids: vargs.options.knownTxids,
934
+ mergeToBeef: beef,
935
+ ignoreStorage: false,
936
+ ignoreServices: true,
937
+ ignoreNewProven: false,
938
+ minProofLevel: undefined
939
+ }
940
+ if (vargs.options.returnTXIDOnly) return undefined
941
+ for (const o of allocatedChange) {
942
+ if (!beef.findTxid(o.txid!) && !vargs.options.knownTxids.find(txid => txid === o.txid)) {
943
+ await storage.getBeefForTransaction(o.txid!, options)
944
+ }
945
+ }
946
+ return trimInputBeef(beef, vargs)
947
+ }