@bsv/wallet-toolbox-client 2.4.3 → 2.4.20

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 (789) hide show
  1. package/LICENSE.txt +58 -0
  2. package/README.md +43 -22
  3. package/out/index.client.cjs +33583 -0
  4. package/out/index.client.cjs.map +1 -0
  5. package/out/index.client.d.cts +10768 -0
  6. package/out/index.client.d.cts.map +1 -0
  7. package/out/index.client.d.mts +10767 -0
  8. package/out/index.client.d.mts.map +1 -0
  9. package/out/index.client.mjs +33377 -0
  10. package/out/index.client.mjs.map +1 -0
  11. package/out/rolldown-runtime-D7D4PA-g.mjs +13 -0
  12. package/package.json +59 -7
  13. package/out/src/CWIStyleWalletManager.d.ts +0 -517
  14. package/out/src/CWIStyleWalletManager.d.ts.map +0 -1
  15. package/out/src/CWIStyleWalletManager.js +0 -1594
  16. package/out/src/CWIStyleWalletManager.js.map +0 -1
  17. package/out/src/SetupClient.d.ts +0 -135
  18. package/out/src/SetupClient.d.ts.map +0 -1
  19. package/out/src/SetupClient.js +0 -226
  20. package/out/src/SetupClient.js.map +0 -1
  21. package/out/src/SetupWallet.d.ts +0 -103
  22. package/out/src/SetupWallet.d.ts.map +0 -1
  23. package/out/src/SetupWallet.js +0 -3
  24. package/out/src/SetupWallet.js.map +0 -1
  25. package/out/src/SimpleWalletManager.d.ts +0 -179
  26. package/out/src/SimpleWalletManager.d.ts.map +0 -1
  27. package/out/src/SimpleWalletManager.js +0 -360
  28. package/out/src/SimpleWalletManager.js.map +0 -1
  29. package/out/src/Wallet.d.ts +0 -254
  30. package/out/src/Wallet.d.ts.map +0 -1
  31. package/out/src/Wallet.js +0 -909
  32. package/out/src/Wallet.js.map +0 -1
  33. package/out/src/WalletAuthenticationManager.d.ts +0 -33
  34. package/out/src/WalletAuthenticationManager.d.ts.map +0 -1
  35. package/out/src/WalletAuthenticationManager.js +0 -126
  36. package/out/src/WalletAuthenticationManager.js.map +0 -1
  37. package/out/src/WalletLogger.d.ts +0 -65
  38. package/out/src/WalletLogger.d.ts.map +0 -1
  39. package/out/src/WalletLogger.js +0 -177
  40. package/out/src/WalletLogger.js.map +0 -1
  41. package/out/src/WalletPermissionsManager.d.ts +0 -981
  42. package/out/src/WalletPermissionsManager.d.ts.map +0 -1
  43. package/out/src/WalletPermissionsManager.js +0 -3744
  44. package/out/src/WalletPermissionsManager.js.map +0 -1
  45. package/out/src/WalletSettingsManager.d.ts +0 -53
  46. package/out/src/WalletSettingsManager.d.ts.map +0 -1
  47. package/out/src/WalletSettingsManager.js +0 -86
  48. package/out/src/WalletSettingsManager.js.map +0 -1
  49. package/out/src/fundWalletP2PKH.d.ts +0 -48
  50. package/out/src/fundWalletP2PKH.d.ts.map +0 -1
  51. package/out/src/fundWalletP2PKH.js +0 -239
  52. package/out/src/fundWalletP2PKH.js.map +0 -1
  53. package/out/src/index.client.d.ts +0 -23
  54. package/out/src/index.client.d.ts.map +0 -1
  55. package/out/src/index.client.js +0 -62
  56. package/out/src/index.client.js.map +0 -1
  57. package/out/src/mockchain/MockChainMigrations.d.ts +0 -20
  58. package/out/src/mockchain/MockChainMigrations.d.ts.map +0 -1
  59. package/out/src/mockchain/MockChainMigrations.js +0 -67
  60. package/out/src/mockchain/MockChainMigrations.js.map +0 -1
  61. package/out/src/mockchain/MockChainStorage.d.ts +0 -58
  62. package/out/src/mockchain/MockChainStorage.d.ts.map +0 -1
  63. package/out/src/mockchain/MockChainStorage.js +0 -102
  64. package/out/src/mockchain/MockChainStorage.js.map +0 -1
  65. package/out/src/mockchain/MockChainTracker.d.ts +0 -28
  66. package/out/src/mockchain/MockChainTracker.d.ts.map +0 -1
  67. package/out/src/mockchain/MockChainTracker.js +0 -97
  68. package/out/src/mockchain/MockChainTracker.js.map +0 -1
  69. package/out/src/mockchain/MockMiner.d.ts +0 -16
  70. package/out/src/mockchain/MockMiner.d.ts.map +0 -1
  71. package/out/src/mockchain/MockMiner.js +0 -137
  72. package/out/src/mockchain/MockMiner.js.map +0 -1
  73. package/out/src/mockchain/MockServices.d.ts +0 -53
  74. package/out/src/mockchain/MockServices.d.ts.map +0 -1
  75. package/out/src/mockchain/MockServices.js +0 -459
  76. package/out/src/mockchain/MockServices.js.map +0 -1
  77. package/out/src/mockchain/merkleTree.d.ts +0 -13
  78. package/out/src/mockchain/merkleTree.d.ts.map +0 -1
  79. package/out/src/mockchain/merkleTree.js +0 -122
  80. package/out/src/mockchain/merkleTree.js.map +0 -1
  81. package/out/src/monitor/Monitor.d.ts +0 -189
  82. package/out/src/monitor/Monitor.d.ts.map +0 -1
  83. package/out/src/monitor/Monitor.js +0 -386
  84. package/out/src/monitor/Monitor.js.map +0 -1
  85. package/out/src/monitor/tasks/TaskArcSSE.d.ts +0 -29
  86. package/out/src/monitor/tasks/TaskArcSSE.d.ts.map +0 -1
  87. package/out/src/monitor/tasks/TaskArcSSE.js +0 -259
  88. package/out/src/monitor/tasks/TaskArcSSE.js.map +0 -1
  89. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts +0 -53
  90. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +0 -1
  91. package/out/src/monitor/tasks/TaskCheckForProofs.js +0 -224
  92. package/out/src/monitor/tasks/TaskCheckForProofs.js.map +0 -1
  93. package/out/src/monitor/tasks/TaskCheckNoSends.d.ts +0 -91
  94. package/out/src/monitor/tasks/TaskCheckNoSends.d.ts.map +0 -1
  95. package/out/src/monitor/tasks/TaskCheckNoSends.js +0 -159
  96. package/out/src/monitor/tasks/TaskCheckNoSends.js.map +0 -1
  97. package/out/src/monitor/tasks/TaskClock.d.ts +0 -14
  98. package/out/src/monitor/tasks/TaskClock.d.ts.map +0 -1
  99. package/out/src/monitor/tasks/TaskClock.js +0 -29
  100. package/out/src/monitor/tasks/TaskClock.js.map +0 -1
  101. package/out/src/monitor/tasks/TaskFailAbandoned.d.ts +0 -20
  102. package/out/src/monitor/tasks/TaskFailAbandoned.d.ts.map +0 -1
  103. package/out/src/monitor/tasks/TaskFailAbandoned.js +0 -53
  104. package/out/src/monitor/tasks/TaskFailAbandoned.js.map +0 -1
  105. package/out/src/monitor/tasks/TaskMineBlock.d.ts +0 -13
  106. package/out/src/monitor/tasks/TaskMineBlock.d.ts.map +0 -1
  107. package/out/src/monitor/tasks/TaskMineBlock.js +0 -30
  108. package/out/src/monitor/tasks/TaskMineBlock.js.map +0 -1
  109. package/out/src/monitor/tasks/TaskMonitorCallHistory.d.ts +0 -12
  110. package/out/src/monitor/tasks/TaskMonitorCallHistory.d.ts.map +0 -1
  111. package/out/src/monitor/tasks/TaskMonitorCallHistory.js +0 -25
  112. package/out/src/monitor/tasks/TaskMonitorCallHistory.js.map +0 -1
  113. package/out/src/monitor/tasks/TaskNewHeader.d.ts +0 -50
  114. package/out/src/monitor/tasks/TaskNewHeader.d.ts.map +0 -1
  115. package/out/src/monitor/tasks/TaskNewHeader.js +0 -97
  116. package/out/src/monitor/tasks/TaskNewHeader.js.map +0 -1
  117. package/out/src/monitor/tasks/TaskPurge.d.ts +0 -45
  118. package/out/src/monitor/tasks/TaskPurge.d.ts.map +0 -1
  119. package/out/src/monitor/tasks/TaskPurge.js +0 -36
  120. package/out/src/monitor/tasks/TaskPurge.js.map +0 -1
  121. package/out/src/monitor/tasks/TaskReorg.d.ts +0 -43
  122. package/out/src/monitor/tasks/TaskReorg.d.ts.map +0 -1
  123. package/out/src/monitor/tasks/TaskReorg.js +0 -84
  124. package/out/src/monitor/tasks/TaskReorg.js.map +0 -1
  125. package/out/src/monitor/tasks/TaskReviewDoubleSpends.d.ts +0 -26
  126. package/out/src/monitor/tasks/TaskReviewDoubleSpends.d.ts.map +0 -1
  127. package/out/src/monitor/tasks/TaskReviewDoubleSpends.js +0 -127
  128. package/out/src/monitor/tasks/TaskReviewDoubleSpends.js.map +0 -1
  129. package/out/src/monitor/tasks/TaskReviewProvenTxs.d.ts +0 -34
  130. package/out/src/monitor/tasks/TaskReviewProvenTxs.d.ts.map +0 -1
  131. package/out/src/monitor/tasks/TaskReviewProvenTxs.js +0 -136
  132. package/out/src/monitor/tasks/TaskReviewProvenTxs.js.map +0 -1
  133. package/out/src/monitor/tasks/TaskReviewStatus.d.ts +0 -26
  134. package/out/src/monitor/tasks/TaskReviewStatus.d.ts.map +0 -1
  135. package/out/src/monitor/tasks/TaskReviewStatus.js +0 -50
  136. package/out/src/monitor/tasks/TaskReviewStatus.js.map +0 -1
  137. package/out/src/monitor/tasks/TaskReviewUtxos.d.ts +0 -23
  138. package/out/src/monitor/tasks/TaskReviewUtxos.d.ts.map +0 -1
  139. package/out/src/monitor/tasks/TaskReviewUtxos.js +0 -75
  140. package/out/src/monitor/tasks/TaskReviewUtxos.js.map +0 -1
  141. package/out/src/monitor/tasks/TaskSendWaiting.d.ts +0 -48
  142. package/out/src/monitor/tasks/TaskSendWaiting.d.ts.map +0 -1
  143. package/out/src/monitor/tasks/TaskSendWaiting.js +0 -202
  144. package/out/src/monitor/tasks/TaskSendWaiting.js.map +0 -1
  145. package/out/src/monitor/tasks/TaskUnFail.d.ts +0 -41
  146. package/out/src/monitor/tasks/TaskUnFail.d.ts.map +0 -1
  147. package/out/src/monitor/tasks/TaskUnFail.js +0 -89
  148. package/out/src/monitor/tasks/TaskUnFail.js.map +0 -1
  149. package/out/src/monitor/tasks/WalletMonitorTask.d.ts +0 -40
  150. package/out/src/monitor/tasks/WalletMonitorTask.d.ts.map +0 -1
  151. package/out/src/monitor/tasks/WalletMonitorTask.js +0 -42
  152. package/out/src/monitor/tasks/WalletMonitorTask.js.map +0 -1
  153. package/out/src/sdk/CertOpsWallet.d.ts +0 -7
  154. package/out/src/sdk/CertOpsWallet.d.ts.map +0 -1
  155. package/out/src/sdk/CertOpsWallet.js +0 -3
  156. package/out/src/sdk/CertOpsWallet.js.map +0 -1
  157. package/out/src/sdk/PrivilegedKeyManager.d.ts +0 -125
  158. package/out/src/sdk/PrivilegedKeyManager.d.ts.map +0 -1
  159. package/out/src/sdk/PrivilegedKeyManager.js +0 -320
  160. package/out/src/sdk/PrivilegedKeyManager.js.map +0 -1
  161. package/out/src/sdk/WERR_errors.d.ts +0 -136
  162. package/out/src/sdk/WERR_errors.d.ts.map +0 -1
  163. package/out/src/sdk/WERR_errors.js +0 -233
  164. package/out/src/sdk/WERR_errors.js.map +0 -1
  165. package/out/src/sdk/WalletError.d.ts +0 -63
  166. package/out/src/sdk/WalletError.d.ts.map +0 -1
  167. package/out/src/sdk/WalletError.js +0 -177
  168. package/out/src/sdk/WalletError.js.map +0 -1
  169. package/out/src/sdk/WalletErrorFromJson.d.ts +0 -12
  170. package/out/src/sdk/WalletErrorFromJson.d.ts.map +0 -1
  171. package/out/src/sdk/WalletErrorFromJson.js +0 -67
  172. package/out/src/sdk/WalletErrorFromJson.js.map +0 -1
  173. package/out/src/sdk/WalletServices.interfaces.d.ts +0 -643
  174. package/out/src/sdk/WalletServices.interfaces.d.ts.map +0 -1
  175. package/out/src/sdk/WalletServices.interfaces.js +0 -3
  176. package/out/src/sdk/WalletServices.interfaces.js.map +0 -1
  177. package/out/src/sdk/WalletSigner.interfaces.d.ts +0 -10
  178. package/out/src/sdk/WalletSigner.interfaces.d.ts.map +0 -1
  179. package/out/src/sdk/WalletSigner.interfaces.js +0 -3
  180. package/out/src/sdk/WalletSigner.interfaces.js.map +0 -1
  181. package/out/src/sdk/WalletStorage.interfaces.d.ts +0 -539
  182. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +0 -1
  183. package/out/src/sdk/WalletStorage.interfaces.js +0 -3
  184. package/out/src/sdk/WalletStorage.interfaces.js.map +0 -1
  185. package/out/src/sdk/index.d.ts +0 -11
  186. package/out/src/sdk/index.d.ts.map +0 -1
  187. package/out/src/sdk/index.js +0 -29
  188. package/out/src/sdk/index.js.map +0 -1
  189. package/out/src/sdk/types.d.ts +0 -178
  190. package/out/src/sdk/types.d.ts.map +0 -1
  191. package/out/src/sdk/types.js +0 -104
  192. package/out/src/sdk/types.js.map +0 -1
  193. package/out/src/services/ServiceCollection.d.ts +0 -79
  194. package/out/src/services/ServiceCollection.d.ts.map +0 -1
  195. package/out/src/services/ServiceCollection.js +0 -197
  196. package/out/src/services/ServiceCollection.js.map +0 -1
  197. package/out/src/services/Services.d.ts +0 -97
  198. package/out/src/services/Services.d.ts.map +0 -1
  199. package/out/src/services/Services.js +0 -729
  200. package/out/src/services/Services.js.map +0 -1
  201. package/out/src/services/chaintracker/BHServiceClient.d.ts +0 -39
  202. package/out/src/services/chaintracker/BHServiceClient.d.ts.map +0 -1
  203. package/out/src/services/chaintracker/BHServiceClient.js +0 -180
  204. package/out/src/services/chaintracker/BHServiceClient.js.map +0 -1
  205. package/out/src/services/chaintracker/ChaintracksChainTracker.d.ts +0 -16
  206. package/out/src/services/chaintracker/ChaintracksChainTracker.d.ts.map +0 -1
  207. package/out/src/services/chaintracker/ChaintracksChainTracker.js +0 -59
  208. package/out/src/services/chaintracker/ChaintracksChainTracker.js.map +0 -1
  209. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.d.ts +0 -57
  210. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.d.ts.map +0 -1
  211. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.js +0 -38
  212. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.js.map +0 -1
  213. package/out/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.d.ts +0 -70
  214. package/out/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.d.ts.map +0 -1
  215. package/out/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.js +0 -3
  216. package/out/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.js.map +0 -1
  217. package/out/src/services/chaintracker/chaintracks/Api/BulkStorageApi.d.ts +0 -81
  218. package/out/src/services/chaintracker/chaintracks/Api/BulkStorageApi.d.ts.map +0 -1
  219. package/out/src/services/chaintracker/chaintracks/Api/BulkStorageApi.js +0 -3
  220. package/out/src/services/chaintracker/chaintracks/Api/BulkStorageApi.js.map +0 -1
  221. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksApi.d.ts +0 -54
  222. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksApi.d.ts.map +0 -1
  223. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksApi.js +0 -3
  224. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksApi.js.map +0 -1
  225. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts +0 -163
  226. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts.map +0 -1
  227. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.js +0 -3
  228. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.js.map +0 -1
  229. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts +0 -18
  230. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts.map +0 -1
  231. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.js +0 -3
  232. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.js.map +0 -1
  233. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.d.ts +0 -55
  234. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.d.ts.map +0 -1
  235. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.js +0 -3
  236. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.js.map +0 -1
  237. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.d.ts +0 -338
  238. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.d.ts.map +0 -1
  239. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.js +0 -3
  240. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.js.map +0 -1
  241. package/out/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.d.ts +0 -19
  242. package/out/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.d.ts.map +0 -1
  243. package/out/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.js +0 -3
  244. package/out/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.js.map +0 -1
  245. package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts +0 -113
  246. package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts.map +0 -1
  247. package/out/src/services/chaintracker/chaintracks/Chaintracks.js +0 -607
  248. package/out/src/services/chaintracker/chaintracks/Chaintracks.js.map +0 -1
  249. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts +0 -38
  250. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts.map +0 -1
  251. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.js +0 -149
  252. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.js.map +0 -1
  253. package/out/src/services/chaintracker/chaintracks/GoChaintracksServiceClient.d.ts +0 -52
  254. package/out/src/services/chaintracker/chaintracks/GoChaintracksServiceClient.d.ts.map +0 -1
  255. package/out/src/services/chaintracker/chaintracks/GoChaintracksServiceClient.js +0 -232
  256. package/out/src/services/chaintracker/chaintracks/GoChaintracksServiceClient.js.map +0 -1
  257. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.d.ts +0 -60
  258. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.d.ts.map +0 -1
  259. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.js +0 -126
  260. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.js.map +0 -1
  261. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.d.ts +0 -75
  262. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.d.ts.map +0 -1
  263. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.js +0 -121
  264. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.js.map +0 -1
  265. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.d.ts +0 -13
  266. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.d.ts.map +0 -1
  267. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.js +0 -20
  268. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.js.map +0 -1
  269. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.d.ts +0 -62
  270. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.d.ts.map +0 -1
  271. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.js +0 -61
  272. package/out/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.js.map +0 -1
  273. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.d.ts +0 -62
  274. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.d.ts.map +0 -1
  275. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.js +0 -44
  276. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.js.map +0 -1
  277. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorChaintracksSSE.d.ts +0 -24
  278. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorChaintracksSSE.d.ts.map +0 -1
  279. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorChaintracksSSE.js +0 -55
  280. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorChaintracksSSE.js.map +0 -1
  281. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.d.ts +0 -64
  282. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.d.ts.map +0 -1
  283. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.js +0 -95
  284. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.js.map +0 -1
  285. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.d.ts +0 -171
  286. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.d.ts.map +0 -1
  287. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.js +0 -487
  288. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.js.map +0 -1
  289. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.d.ts +0 -89
  290. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.d.ts.map +0 -1
  291. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.js +0 -142
  292. package/out/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.js.map +0 -1
  293. package/out/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.d.ts +0 -23
  294. package/out/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.d.ts.map +0 -1
  295. package/out/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.js +0 -63
  296. package/out/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.js.map +0 -1
  297. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.d.ts +0 -68
  298. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.d.ts.map +0 -1
  299. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js +0 -311
  300. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js.map +0 -1
  301. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.d.ts +0 -92
  302. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.d.ts.map +0 -1
  303. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.js +0 -467
  304. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.js.map +0 -1
  305. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.d.ts +0 -37
  306. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.d.ts.map +0 -1
  307. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.js +0 -261
  308. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.js.map +0 -1
  309. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts +0 -22
  310. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts.map +0 -1
  311. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js +0 -54
  312. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js.map +0 -1
  313. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.d.ts +0 -5
  314. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.d.ts.map +0 -1
  315. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.js +0 -29
  316. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.js.map +0 -1
  317. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.d.ts +0 -5
  318. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.d.ts.map +0 -1
  319. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.js +0 -29
  320. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.js.map +0 -1
  321. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.d.ts +0 -13
  322. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.d.ts.map +0 -1
  323. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.js +0 -27
  324. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.js.map +0 -1
  325. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.d.ts +0 -13
  326. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.d.ts.map +0 -1
  327. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.js +0 -27
  328. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.js.map +0 -1
  329. package/out/src/services/chaintracker/chaintracks/index.client.d.ts +0 -4
  330. package/out/src/services/chaintracker/chaintracks/index.client.d.ts.map +0 -1
  331. package/out/src/services/chaintracker/chaintracks/index.client.js +0 -20
  332. package/out/src/services/chaintracker/chaintracks/index.client.js.map +0 -1
  333. package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts +0 -32
  334. package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts.map +0 -1
  335. package/out/src/services/chaintracker/chaintracks/index.mobile.js +0 -70
  336. package/out/src/services/chaintracker/chaintracks/index.mobile.js.map +0 -1
  337. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts +0 -142
  338. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts.map +0 -1
  339. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js +0 -821
  340. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js.map +0 -1
  341. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataReader.d.ts +0 -22
  342. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataReader.d.ts.map +0 -1
  343. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataReader.js +0 -65
  344. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataReader.js.map +0 -1
  345. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.d.ts +0 -78
  346. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.d.ts.map +0 -1
  347. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.js +0 -284
  348. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.js.map +0 -1
  349. package/out/src/services/chaintracker/chaintracks/util/BulkHeaderFile.d.ts +0 -129
  350. package/out/src/services/chaintracker/chaintracks/util/BulkHeaderFile.d.ts.map +0 -1
  351. package/out/src/services/chaintracker/chaintracks/util/BulkHeaderFile.js +0 -168
  352. package/out/src/services/chaintracker/chaintracks/util/BulkHeaderFile.js.map +0 -1
  353. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts +0 -21
  354. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts.map +0 -1
  355. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js +0 -108
  356. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js.map +0 -1
  357. package/out/src/services/chaintracker/chaintracks/util/HeightRange.d.ts +0 -90
  358. package/out/src/services/chaintracker/chaintracks/util/HeightRange.d.ts.map +0 -1
  359. package/out/src/services/chaintracker/chaintracks/util/HeightRange.js +0 -150
  360. package/out/src/services/chaintracker/chaintracks/util/HeightRange.js.map +0 -1
  361. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.d.ts +0 -14
  362. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.d.ts.map +0 -1
  363. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.js +0 -86
  364. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.js.map +0 -1
  365. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.d.ts +0 -159
  366. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.d.ts.map +0 -1
  367. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.js +0 -530
  368. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.js.map +0 -1
  369. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.d.ts +0 -20
  370. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.d.ts.map +0 -1
  371. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.js +0 -31
  372. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.js.map +0 -1
  373. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.d.ts +0 -37
  374. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.d.ts.map +0 -1
  375. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js +0 -429
  376. package/out/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.js.map +0 -1
  377. package/out/src/services/chaintracker/index.client.d.ts +0 -4
  378. package/out/src/services/chaintracker/index.client.d.ts.map +0 -1
  379. package/out/src/services/chaintracker/index.client.js +0 -20
  380. package/out/src/services/chaintracker/index.client.js.map +0 -1
  381. package/out/src/services/createDefaultWalletServicesOptions.d.ts +0 -25
  382. package/out/src/services/createDefaultWalletServicesOptions.d.ts.map +0 -1
  383. package/out/src/services/createDefaultWalletServicesOptions.js +0 -143
  384. package/out/src/services/createDefaultWalletServicesOptions.js.map +0 -1
  385. package/out/src/services/networkConfig.d.ts +0 -29
  386. package/out/src/services/networkConfig.d.ts.map +0 -1
  387. package/out/src/services/networkConfig.js +0 -54
  388. package/out/src/services/networkConfig.js.map +0 -1
  389. package/out/src/services/providers/ARC.d.ts +0 -96
  390. package/out/src/services/providers/ARC.d.ts.map +0 -1
  391. package/out/src/services/providers/ARC.js +0 -332
  392. package/out/src/services/providers/ARC.js.map +0 -1
  393. package/out/src/services/providers/ArcSSEClient.d.ts +0 -58
  394. package/out/src/services/providers/ArcSSEClient.d.ts.map +0 -1
  395. package/out/src/services/providers/ArcSSEClient.js +0 -115
  396. package/out/src/services/providers/ArcSSEClient.js.map +0 -1
  397. package/out/src/services/providers/Arcade.d.ts +0 -73
  398. package/out/src/services/providers/Arcade.d.ts.map +0 -1
  399. package/out/src/services/providers/Arcade.js +0 -330
  400. package/out/src/services/providers/Arcade.js.map +0 -1
  401. package/out/src/services/providers/Bitails.d.ts +0 -50
  402. package/out/src/services/providers/Bitails.d.ts.map +0 -1
  403. package/out/src/services/providers/Bitails.js +0 -237
  404. package/out/src/services/providers/Bitails.js.map +0 -1
  405. package/out/src/services/providers/SdkWhatsOnChain.d.ts +0 -21
  406. package/out/src/services/providers/SdkWhatsOnChain.d.ts.map +0 -1
  407. package/out/src/services/providers/SdkWhatsOnChain.js +0 -82
  408. package/out/src/services/providers/SdkWhatsOnChain.js.map +0 -1
  409. package/out/src/services/providers/WhatsOnChain.d.ts +0 -132
  410. package/out/src/services/providers/WhatsOnChain.d.ts.map +0 -1
  411. package/out/src/services/providers/WhatsOnChain.js +0 -558
  412. package/out/src/services/providers/WhatsOnChain.js.map +0 -1
  413. package/out/src/services/providers/exchangeRates.d.ts +0 -12
  414. package/out/src/services/providers/exchangeRates.d.ts.map +0 -1
  415. package/out/src/services/providers/exchangeRates.js +0 -244
  416. package/out/src/services/providers/exchangeRates.js.map +0 -1
  417. package/out/src/services/providers/getBeefForTxid.d.ts +0 -4
  418. package/out/src/services/providers/getBeefForTxid.d.ts.map +0 -1
  419. package/out/src/services/providers/getBeefForTxid.js +0 -287
  420. package/out/src/services/providers/getBeefForTxid.js.map +0 -1
  421. package/out/src/services/providers/whatsOnChainHelpers.d.ts +0 -68
  422. package/out/src/services/providers/whatsOnChainHelpers.d.ts.map +0 -1
  423. package/out/src/services/providers/whatsOnChainHelpers.js +0 -147
  424. package/out/src/services/providers/whatsOnChainHelpers.js.map +0 -1
  425. package/out/src/signer/WalletSigner.d.ts +0 -11
  426. package/out/src/signer/WalletSigner.d.ts.map +0 -1
  427. package/out/src/signer/WalletSigner.js +0 -16
  428. package/out/src/signer/WalletSigner.js.map +0 -1
  429. package/out/src/signer/methods/acquireDirectCertificate.d.ts +0 -5
  430. package/out/src/signer/methods/acquireDirectCertificate.d.ts.map +0 -1
  431. package/out/src/signer/methods/acquireDirectCertificate.js +0 -44
  432. package/out/src/signer/methods/acquireDirectCertificate.js.map +0 -1
  433. package/out/src/signer/methods/buildSignableTransaction.d.ts +0 -69
  434. package/out/src/signer/methods/buildSignableTransaction.d.ts.map +0 -1
  435. package/out/src/signer/methods/buildSignableTransaction.js +0 -267
  436. package/out/src/signer/methods/buildSignableTransaction.js.map +0 -1
  437. package/out/src/signer/methods/completeSignedTransaction.d.ts +0 -10
  438. package/out/src/signer/methods/completeSignedTransaction.d.ts.map +0 -1
  439. package/out/src/signer/methods/completeSignedTransaction.js +0 -106
  440. package/out/src/signer/methods/completeSignedTransaction.js.map +0 -1
  441. package/out/src/signer/methods/createAction.d.ts +0 -14
  442. package/out/src/signer/methods/createAction.d.ts.map +0 -1
  443. package/out/src/signer/methods/createAction.js +0 -123
  444. package/out/src/signer/methods/createAction.js.map +0 -1
  445. package/out/src/signer/methods/internalizeAction.d.ts +0 -43
  446. package/out/src/signer/methods/internalizeAction.d.ts.map +0 -1
  447. package/out/src/signer/methods/internalizeAction.js +0 -109
  448. package/out/src/signer/methods/internalizeAction.js.map +0 -1
  449. package/out/src/signer/methods/proveCertificate.d.ts +0 -5
  450. package/out/src/signer/methods/proveCertificate.d.ts.map +0 -1
  451. package/out/src/signer/methods/proveCertificate.js +0 -29
  452. package/out/src/signer/methods/proveCertificate.js.map +0 -1
  453. package/out/src/signer/methods/resultBeef.d.ts +0 -4
  454. package/out/src/signer/methods/resultBeef.d.ts.map +0 -1
  455. package/out/src/signer/methods/resultBeef.js +0 -12
  456. package/out/src/signer/methods/resultBeef.js.map +0 -1
  457. package/out/src/signer/methods/signAction.d.ts +0 -11
  458. package/out/src/signer/methods/signAction.d.ts.map +0 -1
  459. package/out/src/signer/methods/signAction.js +0 -43
  460. package/out/src/signer/methods/signAction.js.map +0 -1
  461. package/out/src/storage/StorageIdb.d.ts +0 -234
  462. package/out/src/storage/StorageIdb.d.ts.map +0 -1
  463. package/out/src/storage/StorageIdb.js +0 -1701
  464. package/out/src/storage/StorageIdb.js.map +0 -1
  465. package/out/src/storage/StorageProvider.d.ts +0 -281
  466. package/out/src/storage/StorageProvider.d.ts.map +0 -1
  467. package/out/src/storage/StorageProvider.js +0 -924
  468. package/out/src/storage/StorageProvider.js.map +0 -1
  469. package/out/src/storage/StorageReader.d.ts +0 -77
  470. package/out/src/storage/StorageReader.d.ts.map +0 -1
  471. package/out/src/storage/StorageReader.js +0 -166
  472. package/out/src/storage/StorageReader.js.map +0 -1
  473. package/out/src/storage/StorageReaderWriter.d.ts +0 -102
  474. package/out/src/storage/StorageReaderWriter.d.ts.map +0 -1
  475. package/out/src/storage/StorageReaderWriter.js +0 -335
  476. package/out/src/storage/StorageReaderWriter.js.map +0 -1
  477. package/out/src/storage/StorageSyncReader.d.ts +0 -18
  478. package/out/src/storage/StorageSyncReader.d.ts.map +0 -1
  479. package/out/src/storage/StorageSyncReader.js +0 -73
  480. package/out/src/storage/StorageSyncReader.js.map +0 -1
  481. package/out/src/storage/WalletStorageManager.d.ts +0 -217
  482. package/out/src/storage/WalletStorageManager.d.ts.map +0 -1
  483. package/out/src/storage/WalletStorageManager.js +0 -830
  484. package/out/src/storage/WalletStorageManager.js.map +0 -1
  485. package/out/src/storage/idbHelpers.d.ts +0 -42
  486. package/out/src/storage/idbHelpers.d.ts.map +0 -1
  487. package/out/src/storage/idbHelpers.js +0 -374
  488. package/out/src/storage/idbHelpers.js.map +0 -1
  489. package/out/src/storage/index.client.d.ts +0 -12
  490. package/out/src/storage/index.client.d.ts.map +0 -1
  491. package/out/src/storage/index.client.js +0 -28
  492. package/out/src/storage/index.client.js.map +0 -1
  493. package/out/src/storage/methods/ListActionsSpecOp.d.ts +0 -23
  494. package/out/src/storage/methods/ListActionsSpecOp.d.ts.map +0 -1
  495. package/out/src/storage/methods/ListActionsSpecOp.js +0 -129
  496. package/out/src/storage/methods/ListActionsSpecOp.js.map +0 -1
  497. package/out/src/storage/methods/ListOutputsSpecOp.d.ts +0 -42
  498. package/out/src/storage/methods/ListOutputsSpecOp.d.ts.map +0 -1
  499. package/out/src/storage/methods/ListOutputsSpecOp.js +0 -154
  500. package/out/src/storage/methods/ListOutputsSpecOp.js.map +0 -1
  501. package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts +0 -124
  502. package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +0 -1
  503. package/out/src/storage/methods/attemptToPostReqsToNetwork.js +0 -490
  504. package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +0 -1
  505. package/out/src/storage/methods/createAction.d.ts +0 -13
  506. package/out/src/storage/methods/createAction.d.ts.map +0 -1
  507. package/out/src/storage/methods/createAction.js +0 -793
  508. package/out/src/storage/methods/createAction.js.map +0 -1
  509. package/out/src/storage/methods/generateChange.d.ts +0 -121
  510. package/out/src/storage/methods/generateChange.d.ts.map +0 -1
  511. package/out/src/storage/methods/generateChange.js +0 -496
  512. package/out/src/storage/methods/generateChange.js.map +0 -1
  513. package/out/src/storage/methods/getBeefForTransaction.d.ts +0 -23
  514. package/out/src/storage/methods/getBeefForTransaction.d.ts.map +0 -1
  515. package/out/src/storage/methods/getBeefForTransaction.js +0 -132
  516. package/out/src/storage/methods/getBeefForTransaction.js.map +0 -1
  517. package/out/src/storage/methods/getSyncChunk.d.ts +0 -10
  518. package/out/src/storage/methods/getSyncChunk.d.ts.map +0 -1
  519. package/out/src/storage/methods/getSyncChunk.js +0 -273
  520. package/out/src/storage/methods/getSyncChunk.js.map +0 -1
  521. package/out/src/storage/methods/internalizeAction.d.ts +0 -102
  522. package/out/src/storage/methods/internalizeAction.d.ts.map +0 -1
  523. package/out/src/storage/methods/internalizeAction.js +0 -670
  524. package/out/src/storage/methods/internalizeAction.js.map +0 -1
  525. package/out/src/storage/methods/listActionsIdb.d.ts +0 -5
  526. package/out/src/storage/methods/listActionsIdb.d.ts.map +0 -1
  527. package/out/src/storage/methods/listActionsIdb.js +0 -142
  528. package/out/src/storage/methods/listActionsIdb.js.map +0 -1
  529. package/out/src/storage/methods/listCertificates.d.ts +0 -5
  530. package/out/src/storage/methods/listCertificates.d.ts.map +0 -1
  531. package/out/src/storage/methods/listCertificates.js +0 -68
  532. package/out/src/storage/methods/listCertificates.js.map +0 -1
  533. package/out/src/storage/methods/listOutputsIdb.d.ts +0 -5
  534. package/out/src/storage/methods/listOutputsIdb.d.ts.map +0 -1
  535. package/out/src/storage/methods/listOutputsIdb.js +0 -195
  536. package/out/src/storage/methods/listOutputsIdb.js.map +0 -1
  537. package/out/src/storage/methods/managedChange.d.ts +0 -26
  538. package/out/src/storage/methods/managedChange.d.ts.map +0 -1
  539. package/out/src/storage/methods/managedChange.js +0 -43
  540. package/out/src/storage/methods/managedChange.js.map +0 -1
  541. package/out/src/storage/methods/offsetKey.d.ts +0 -24
  542. package/out/src/storage/methods/offsetKey.d.ts.map +0 -1
  543. package/out/src/storage/methods/offsetKey.js +0 -65
  544. package/out/src/storage/methods/offsetKey.js.map +0 -1
  545. package/out/src/storage/methods/processAction.d.ts +0 -43
  546. package/out/src/storage/methods/processAction.d.ts.map +0 -1
  547. package/out/src/storage/methods/processAction.js +0 -313
  548. package/out/src/storage/methods/processAction.js.map +0 -1
  549. package/out/src/storage/methods/purgeDataIdb.d.ts +0 -4
  550. package/out/src/storage/methods/purgeDataIdb.d.ts.map +0 -1
  551. package/out/src/storage/methods/purgeDataIdb.js +0 -9
  552. package/out/src/storage/methods/purgeDataIdb.js.map +0 -1
  553. package/out/src/storage/methods/reviewStatusIdb.d.ts +0 -20
  554. package/out/src/storage/methods/reviewStatusIdb.d.ts.map +0 -1
  555. package/out/src/storage/methods/reviewStatusIdb.js +0 -76
  556. package/out/src/storage/methods/reviewStatusIdb.js.map +0 -1
  557. package/out/src/storage/methods/utils.d.ts +0 -25
  558. package/out/src/storage/methods/utils.d.ts.map +0 -1
  559. package/out/src/storage/methods/utils.js +0 -59
  560. package/out/src/storage/methods/utils.js.map +0 -1
  561. package/out/src/storage/portable/index.d.ts +0 -55
  562. package/out/src/storage/portable/index.d.ts.map +0 -1
  563. package/out/src/storage/portable/index.js +0 -872
  564. package/out/src/storage/portable/index.js.map +0 -1
  565. package/out/src/storage/remoting/BinaryJson.d.ts +0 -10
  566. package/out/src/storage/remoting/BinaryJson.d.ts.map +0 -1
  567. package/out/src/storage/remoting/BinaryJson.js +0 -172
  568. package/out/src/storage/remoting/BinaryJson.js.map +0 -1
  569. package/out/src/storage/remoting/StorageClient.d.ts +0 -26
  570. package/out/src/storage/remoting/StorageClient.d.ts.map +0 -1
  571. package/out/src/storage/remoting/StorageClient.js +0 -98
  572. package/out/src/storage/remoting/StorageClient.js.map +0 -1
  573. package/out/src/storage/remoting/StorageClientBase.d.ts +0 -299
  574. package/out/src/storage/remoting/StorageClientBase.d.ts.map +0 -1
  575. package/out/src/storage/remoting/StorageClientBase.js +0 -381
  576. package/out/src/storage/remoting/StorageClientBase.js.map +0 -1
  577. package/out/src/storage/remoting/entityValidationHelpers.d.ts +0 -29
  578. package/out/src/storage/remoting/entityValidationHelpers.d.ts.map +0 -1
  579. package/out/src/storage/remoting/entityValidationHelpers.js +0 -91
  580. package/out/src/storage/remoting/entityValidationHelpers.js.map +0 -1
  581. package/out/src/storage/schema/StorageIdbSchema.d.ts +0 -133
  582. package/out/src/storage/schema/StorageIdbSchema.d.ts.map +0 -1
  583. package/out/src/storage/schema/StorageIdbSchema.js +0 -3
  584. package/out/src/storage/schema/StorageIdbSchema.js.map +0 -1
  585. package/out/src/storage/schema/entities/EntityBase.d.ts +0 -106
  586. package/out/src/storage/schema/entities/EntityBase.d.ts.map +0 -1
  587. package/out/src/storage/schema/entities/EntityBase.js +0 -101
  588. package/out/src/storage/schema/entities/EntityBase.js.map +0 -1
  589. package/out/src/storage/schema/entities/EntityCertificate.d.ts +0 -44
  590. package/out/src/storage/schema/entities/EntityCertificate.d.ts.map +0 -1
  591. package/out/src/storage/schema/entities/EntityCertificate.js +0 -163
  592. package/out/src/storage/schema/entities/EntityCertificate.js.map +0 -1
  593. package/out/src/storage/schema/entities/EntityCertificateField.d.ts +0 -33
  594. package/out/src/storage/schema/entities/EntityCertificateField.d.ts.map +0 -1
  595. package/out/src/storage/schema/entities/EntityCertificateField.js +0 -110
  596. package/out/src/storage/schema/entities/EntityCertificateField.js.map +0 -1
  597. package/out/src/storage/schema/entities/EntityCommission.d.ts +0 -38
  598. package/out/src/storage/schema/entities/EntityCommission.d.ts.map +0 -1
  599. package/out/src/storage/schema/entities/EntityCommission.js +0 -125
  600. package/out/src/storage/schema/entities/EntityCommission.js.map +0 -1
  601. package/out/src/storage/schema/entities/EntityOutput.d.ts +0 -68
  602. package/out/src/storage/schema/entities/EntityOutput.d.ts.map +0 -1
  603. package/out/src/storage/schema/entities/EntityOutput.js +0 -261
  604. package/out/src/storage/schema/entities/EntityOutput.js.map +0 -1
  605. package/out/src/storage/schema/entities/EntityOutputBasket.d.ts +0 -36
  606. package/out/src/storage/schema/entities/EntityOutputBasket.d.ts.map +0 -1
  607. package/out/src/storage/schema/entities/EntityOutputBasket.js +0 -133
  608. package/out/src/storage/schema/entities/EntityOutputBasket.js.map +0 -1
  609. package/out/src/storage/schema/entities/EntityOutputTag.d.ts +0 -32
  610. package/out/src/storage/schema/entities/EntityOutputTag.d.ts.map +0 -1
  611. package/out/src/storage/schema/entities/EntityOutputTag.js +0 -104
  612. package/out/src/storage/schema/entities/EntityOutputTag.js.map +0 -1
  613. package/out/src/storage/schema/entities/EntityOutputTagMap.d.ts +0 -29
  614. package/out/src/storage/schema/entities/EntityOutputTagMap.d.ts.map +0 -1
  615. package/out/src/storage/schema/entities/EntityOutputTagMap.js +0 -96
  616. package/out/src/storage/schema/entities/EntityOutputTagMap.js.map +0 -1
  617. package/out/src/storage/schema/entities/EntityProvenTx.d.ts +0 -87
  618. package/out/src/storage/schema/entities/EntityProvenTx.d.ts.map +0 -1
  619. package/out/src/storage/schema/entities/EntityProvenTx.js +0 -276
  620. package/out/src/storage/schema/entities/EntityProvenTx.js.map +0 -1
  621. package/out/src/storage/schema/entities/EntityProvenTxReq.d.ts +0 -148
  622. package/out/src/storage/schema/entities/EntityProvenTxReq.d.ts.map +0 -1
  623. package/out/src/storage/schema/entities/EntityProvenTxReq.js +0 -570
  624. package/out/src/storage/schema/entities/EntityProvenTxReq.js.map +0 -1
  625. package/out/src/storage/schema/entities/EntitySyncState.d.ts +0 -69
  626. package/out/src/storage/schema/entities/EntitySyncState.d.ts.map +0 -1
  627. package/out/src/storage/schema/entities/EntitySyncState.js +0 -338
  628. package/out/src/storage/schema/entities/EntitySyncState.js.map +0 -1
  629. package/out/src/storage/schema/entities/EntityTransaction.d.ts +0 -71
  630. package/out/src/storage/schema/entities/EntityTransaction.d.ts.map +0 -1
  631. package/out/src/storage/schema/entities/EntityTransaction.js +0 -265
  632. package/out/src/storage/schema/entities/EntityTransaction.js.map +0 -1
  633. package/out/src/storage/schema/entities/EntityTxLabel.d.ts +0 -32
  634. package/out/src/storage/schema/entities/EntityTxLabel.d.ts.map +0 -1
  635. package/out/src/storage/schema/entities/EntityTxLabel.js +0 -104
  636. package/out/src/storage/schema/entities/EntityTxLabel.js.map +0 -1
  637. package/out/src/storage/schema/entities/EntityTxLabelMap.d.ts +0 -29
  638. package/out/src/storage/schema/entities/EntityTxLabelMap.d.ts.map +0 -1
  639. package/out/src/storage/schema/entities/EntityTxLabelMap.js +0 -96
  640. package/out/src/storage/schema/entities/EntityTxLabelMap.js.map +0 -1
  641. package/out/src/storage/schema/entities/EntityUser.d.ts +0 -30
  642. package/out/src/storage/schema/entities/EntityUser.d.ts.map +0 -1
  643. package/out/src/storage/schema/entities/EntityUser.js +0 -99
  644. package/out/src/storage/schema/entities/EntityUser.js.map +0 -1
  645. package/out/src/storage/schema/entities/MergeEntity.d.ts +0 -34
  646. package/out/src/storage/schema/entities/MergeEntity.d.ts.map +0 -1
  647. package/out/src/storage/schema/entities/MergeEntity.js +0 -67
  648. package/out/src/storage/schema/entities/MergeEntity.js.map +0 -1
  649. package/out/src/storage/schema/entities/index.d.ts +0 -17
  650. package/out/src/storage/schema/entities/index.d.ts.map +0 -1
  651. package/out/src/storage/schema/entities/index.js +0 -33
  652. package/out/src/storage/schema/entities/index.js.map +0 -1
  653. package/out/src/storage/schema/tables/TableAuthSession.d.ts +0 -20
  654. package/out/src/storage/schema/tables/TableAuthSession.d.ts.map +0 -1
  655. package/out/src/storage/schema/tables/TableAuthSession.js +0 -15
  656. package/out/src/storage/schema/tables/TableAuthSession.js.map +0 -1
  657. package/out/src/storage/schema/tables/TableCertificate.d.ts +0 -21
  658. package/out/src/storage/schema/tables/TableCertificate.d.ts.map +0 -1
  659. package/out/src/storage/schema/tables/TableCertificate.js +0 -3
  660. package/out/src/storage/schema/tables/TableCertificate.js.map +0 -1
  661. package/out/src/storage/schema/tables/TableCertificateField.d.ts +0 -12
  662. package/out/src/storage/schema/tables/TableCertificateField.d.ts.map +0 -1
  663. package/out/src/storage/schema/tables/TableCertificateField.js +0 -3
  664. package/out/src/storage/schema/tables/TableCertificateField.js.map +0 -1
  665. package/out/src/storage/schema/tables/TableCommission.d.ts +0 -13
  666. package/out/src/storage/schema/tables/TableCommission.d.ts.map +0 -1
  667. package/out/src/storage/schema/tables/TableCommission.js +0 -3
  668. package/out/src/storage/schema/tables/TableCommission.js.map +0 -1
  669. package/out/src/storage/schema/tables/TableMonitorEvent.d.ts +0 -9
  670. package/out/src/storage/schema/tables/TableMonitorEvent.d.ts.map +0 -1
  671. package/out/src/storage/schema/tables/TableMonitorEvent.js +0 -3
  672. package/out/src/storage/schema/tables/TableMonitorEvent.js.map +0 -1
  673. package/out/src/storage/schema/tables/TableOutput.d.ts +0 -37
  674. package/out/src/storage/schema/tables/TableOutput.d.ts.map +0 -1
  675. package/out/src/storage/schema/tables/TableOutput.js +0 -31
  676. package/out/src/storage/schema/tables/TableOutput.js.map +0 -1
  677. package/out/src/storage/schema/tables/TableOutputBasket.d.ts +0 -12
  678. package/out/src/storage/schema/tables/TableOutputBasket.d.ts.map +0 -1
  679. package/out/src/storage/schema/tables/TableOutputBasket.js +0 -3
  680. package/out/src/storage/schema/tables/TableOutputBasket.js.map +0 -1
  681. package/out/src/storage/schema/tables/TableOutputTag.d.ts +0 -10
  682. package/out/src/storage/schema/tables/TableOutputTag.d.ts.map +0 -1
  683. package/out/src/storage/schema/tables/TableOutputTag.js +0 -3
  684. package/out/src/storage/schema/tables/TableOutputTag.js.map +0 -1
  685. package/out/src/storage/schema/tables/TableOutputTagMap.d.ts +0 -9
  686. package/out/src/storage/schema/tables/TableOutputTagMap.d.ts.map +0 -1
  687. package/out/src/storage/schema/tables/TableOutputTagMap.js +0 -3
  688. package/out/src/storage/schema/tables/TableOutputTagMap.js.map +0 -1
  689. package/out/src/storage/schema/tables/TableProvenTx.d.ts +0 -14
  690. package/out/src/storage/schema/tables/TableProvenTx.d.ts.map +0 -1
  691. package/out/src/storage/schema/tables/TableProvenTx.js +0 -3
  692. package/out/src/storage/schema/tables/TableProvenTx.js.map +0 -1
  693. package/out/src/storage/schema/tables/TableProvenTxReq.d.ts +0 -87
  694. package/out/src/storage/schema/tables/TableProvenTxReq.d.ts.map +0 -1
  695. package/out/src/storage/schema/tables/TableProvenTxReq.js +0 -3
  696. package/out/src/storage/schema/tables/TableProvenTxReq.js.map +0 -1
  697. package/out/src/storage/schema/tables/TableSettings.d.ts +0 -17
  698. package/out/src/storage/schema/tables/TableSettings.d.ts.map +0 -1
  699. package/out/src/storage/schema/tables/TableSettings.js +0 -3
  700. package/out/src/storage/schema/tables/TableSettings.js.map +0 -1
  701. package/out/src/storage/schema/tables/TableSyncState.d.ts +0 -18
  702. package/out/src/storage/schema/tables/TableSyncState.d.ts.map +0 -1
  703. package/out/src/storage/schema/tables/TableSyncState.js +0 -3
  704. package/out/src/storage/schema/tables/TableSyncState.js.map +0 -1
  705. package/out/src/storage/schema/tables/TableTransaction.d.ts +0 -37
  706. package/out/src/storage/schema/tables/TableTransaction.d.ts.map +0 -1
  707. package/out/src/storage/schema/tables/TableTransaction.js +0 -21
  708. package/out/src/storage/schema/tables/TableTransaction.js.map +0 -1
  709. package/out/src/storage/schema/tables/TableTxLabel.d.ts +0 -10
  710. package/out/src/storage/schema/tables/TableTxLabel.d.ts.map +0 -1
  711. package/out/src/storage/schema/tables/TableTxLabel.js +0 -3
  712. package/out/src/storage/schema/tables/TableTxLabel.js.map +0 -1
  713. package/out/src/storage/schema/tables/TableTxLabelMap.d.ts +0 -9
  714. package/out/src/storage/schema/tables/TableTxLabelMap.d.ts.map +0 -1
  715. package/out/src/storage/schema/tables/TableTxLabelMap.js +0 -3
  716. package/out/src/storage/schema/tables/TableTxLabelMap.js.map +0 -1
  717. package/out/src/storage/schema/tables/TableUser.d.ts +0 -16
  718. package/out/src/storage/schema/tables/TableUser.d.ts.map +0 -1
  719. package/out/src/storage/schema/tables/TableUser.js +0 -3
  720. package/out/src/storage/schema/tables/TableUser.js.map +0 -1
  721. package/out/src/storage/schema/tables/index.d.ts +0 -18
  722. package/out/src/storage/schema/tables/index.d.ts.map +0 -1
  723. package/out/src/storage/schema/tables/index.js +0 -34
  724. package/out/src/storage/schema/tables/index.js.map +0 -1
  725. package/out/src/storage/storageProviderHelpers.d.ts +0 -35
  726. package/out/src/storage/storageProviderHelpers.d.ts.map +0 -1
  727. package/out/src/storage/storageProviderHelpers.js +0 -100
  728. package/out/src/storage/storageProviderHelpers.js.map +0 -1
  729. package/out/src/utility/ScriptTemplateBRC29.d.ts +0 -25
  730. package/out/src/utility/ScriptTemplateBRC29.d.ts.map +0 -1
  731. package/out/src/utility/ScriptTemplateBRC29.js +0 -51
  732. package/out/src/utility/ScriptTemplateBRC29.js.map +0 -1
  733. package/out/src/utility/aggregateResults.d.ts +0 -8
  734. package/out/src/utility/aggregateResults.d.ts.map +0 -1
  735. package/out/src/utility/aggregateResults.js +0 -59
  736. package/out/src/utility/aggregateResults.js.map +0 -1
  737. package/out/src/utility/brc114ActionTimeLabels.d.ts +0 -9
  738. package/out/src/utility/brc114ActionTimeLabels.d.ts.map +0 -1
  739. package/out/src/utility/brc114ActionTimeLabels.js +0 -58
  740. package/out/src/utility/brc114ActionTimeLabels.js.map +0 -1
  741. package/out/src/utility/identityUtils.d.ts +0 -37
  742. package/out/src/utility/identityUtils.d.ts.map +0 -1
  743. package/out/src/utility/identityUtils.js +0 -162
  744. package/out/src/utility/identityUtils.js.map +0 -1
  745. package/out/src/utility/index.client.d.ts +0 -8
  746. package/out/src/utility/index.client.d.ts.map +0 -1
  747. package/out/src/utility/index.client.js +0 -24
  748. package/out/src/utility/index.client.js.map +0 -1
  749. package/out/src/utility/parseTxScriptOffsets.d.ts +0 -14
  750. package/out/src/utility/parseTxScriptOffsets.d.ts.map +0 -1
  751. package/out/src/utility/parseTxScriptOffsets.js +0 -26
  752. package/out/src/utility/parseTxScriptOffsets.js.map +0 -1
  753. package/out/src/utility/stampLog.d.ts +0 -18
  754. package/out/src/utility/stampLog.d.ts.map +0 -1
  755. package/out/src/utility/stampLog.js +0 -74
  756. package/out/src/utility/stampLog.js.map +0 -1
  757. package/out/src/utility/tscProofToMerklePath.d.ts +0 -8
  758. package/out/src/utility/tscProofToMerklePath.d.ts.map +0 -1
  759. package/out/src/utility/tscProofToMerklePath.js +0 -41
  760. package/out/src/utility/tscProofToMerklePath.js.map +0 -1
  761. package/out/src/utility/utilityHelpers.d.ts +0 -142
  762. package/out/src/utility/utilityHelpers.d.ts.map +0 -1
  763. package/out/src/utility/utilityHelpers.js +0 -319
  764. package/out/src/utility/utilityHelpers.js.map +0 -1
  765. package/out/src/utility/utilityHelpers.noBuffer.d.ts +0 -30
  766. package/out/src/utility/utilityHelpers.noBuffer.d.ts.map +0 -1
  767. package/out/src/utility/utilityHelpers.noBuffer.js +0 -64
  768. package/out/src/utility/utilityHelpers.noBuffer.js.map +0 -1
  769. package/out/src/wab-client/WABClient.d.ts +0 -114
  770. package/out/src/wab-client/WABClient.d.ts.map +0 -1
  771. package/out/src/wab-client/WABClient.js +0 -188
  772. package/out/src/wab-client/WABClient.js.map +0 -1
  773. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts +0 -41
  774. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts.map +0 -1
  775. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.js +0 -49
  776. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.js.map +0 -1
  777. package/out/src/wab-client/auth-method-interactors/DevConsoleInteractor.d.ts +0 -11
  778. package/out/src/wab-client/auth-method-interactors/DevConsoleInteractor.d.ts.map +0 -1
  779. package/out/src/wab-client/auth-method-interactors/DevConsoleInteractor.js +0 -15
  780. package/out/src/wab-client/auth-method-interactors/DevConsoleInteractor.js.map +0 -1
  781. package/out/src/wab-client/auth-method-interactors/PersonaIDInteractor.d.ts +0 -5
  782. package/out/src/wab-client/auth-method-interactors/PersonaIDInteractor.d.ts.map +0 -1
  783. package/out/src/wab-client/auth-method-interactors/PersonaIDInteractor.js +0 -9
  784. package/out/src/wab-client/auth-method-interactors/PersonaIDInteractor.js.map +0 -1
  785. package/out/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.d.ts +0 -10
  786. package/out/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.d.ts.map +0 -1
  787. package/out/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.js +0 -14
  788. package/out/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.js.map +0 -1
  789. package/out/tsconfig.client.tsbuildinfo +0 -1
@@ -1,3744 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WalletPermissionsManager = void 0;
4
- const sdk_1 = require("@bsv/sdk");
5
- const brc114ActionTimeLabels_1 = require("./utility/brc114ActionTimeLabels");
6
- /// /// TODO: ADD SUPPORT FOR ADMIN COUNTERPARTIES BASED ON WALLET STORAGE
7
- /// /// PROHIBITION OF SPECIAL OPERATIONS IS ALSO CRITICAL.
8
- /// /// !!!!!!!! SECURITY-CRITICAL ADDITION — DO NOT USE UNTIL IMPLEMENTED.
9
- function deepEqual(object1, object2) {
10
- if (object1 === null || object1 === undefined || object2 === null || object2 === undefined) {
11
- return object1 === object2;
12
- }
13
- const keys1 = Object.keys(object1);
14
- const keys2 = Object.keys(object2);
15
- if (keys1.length !== keys2.length) {
16
- return false;
17
- }
18
- for (const key of keys1) {
19
- const val1 = object1[key];
20
- const val2 = object2[key];
21
- const areObjects = isObject(val1) && isObject(val2);
22
- if ((areObjects && !deepEqual(val1, val2)) || (!areObjects && val1 !== val2)) {
23
- return false;
24
- }
25
- }
26
- return true;
27
- }
28
- function isObject(object) {
29
- return object != null && typeof object === 'object';
30
- }
31
- /**
32
- * A map from each permission type to a special "admin basket" name used for storing
33
- * the tokens. The tokens themselves are unspent transaction outputs (UTXOs) with a
34
- * specialized PushDrop script that references the originator, expiry, etc.
35
- */
36
- const BASKET_MAP = {
37
- protocol: 'admin protocol-permission',
38
- basket: 'admin basket-access',
39
- certificate: 'admin certificate-access',
40
- spending: 'admin spending-authorization'
41
- };
42
- /**
43
- * @class WalletPermissionsManager
44
- *
45
- * Wraps an underlying BRC-100 `Wallet` implementation with permissions management capabilities.
46
- * The manager intercepts calls from external applications (identified by originators), checks if the request is allowed,
47
- * and if not, orchestrates user permission flows. It creates or renews on-chain tokens in special
48
- * admin baskets to track these authorizations. Finally, it proxies the actual call to the underlying wallet.
49
- *
50
- * ### Key Responsibilities:
51
- * - **Permission Checking**: Before standard wallet operations (e.g. `encrypt`),
52
- * the manager checks if a valid permission token exists. If not, it attempts to request permission from the user.
53
- * - **On-Chain Tokens**: When permission is granted, the manager stores it as an unspent "PushDrop" output.
54
- * This can be spent later to revoke or renew the permission.
55
- * - **Callbacks**: The manager triggers user-defined callbacks on permission requests (to show a UI prompt),
56
- * on grants/denials, and on internal processes.
57
- *
58
- * ### Implementation Notes:
59
- * - The manager follows the BRC-100 `createAction` + `signAction` pattern for building or spending these tokens.
60
- * - Token revocation or renewal uses standard BRC-100 flows: we build a transaction that consumes
61
- * the old token UTXO and outputs a new one (or none, if fully revoked).
62
- */
63
- class WalletPermissionsManager {
64
- /** A reference to the BRC-100 wallet instance. */
65
- underlying;
66
- /** The "admin" domain or FQDN that is implicitly allowed to do everything. */
67
- adminOriginator;
68
- /**
69
- * Event callbacks that external code can subscribe to, e.g. to show a UI prompt
70
- * or log events. Each event can have multiple handlers.
71
- */
72
- callbacks = {
73
- onProtocolPermissionRequested: [],
74
- onBasketAccessRequested: [],
75
- onCertificateAccessRequested: [],
76
- onSpendingAuthorizationRequested: [],
77
- onGroupedPermissionRequested: [],
78
- onCounterpartyPermissionRequested: []
79
- };
80
- /**
81
- * We queue parallel requests for the same resource so that only one
82
- * user prompt is created for a single resource. If multiple calls come
83
- * in at once for the same "protocol:domain:privileged:counterparty" etc.,
84
- * they get merged.
85
- *
86
- * The key is a string derived from the operation; the value is an object with a reference to the
87
- * associated request and an array of pending promise resolve/reject pairs, one for each active
88
- * operation that's waiting on the particular resource described by the key.
89
- */
90
- activeRequests = new Map();
91
- /** Cache recently confirmed permissions to avoid repeated lookups. */
92
- permissionCache = new Map();
93
- recentGrants = new Map();
94
- /**
95
- * Token mints currently being written on-chain, keyed by permission cache
96
- * key. A granted permission is only cached once its token finishes minting
97
- * (network seconds); ensures arriving in that window await the in-flight
98
- * mint instead of re-prompting the user for a permission they just granted.
99
- * Stored promises never reject.
100
- */
101
- mintsInFlight = new Map();
102
- manifestCache = new Map();
103
- manifestFetchInProgress = new Map();
104
- static MANIFEST_CACHE_TTL_MS = 5 * 60 * 1000;
105
- groupedPermissionFlowTail = new Map();
106
- pactEstablishedCache = new Map();
107
- /**
108
- * Parsed BEEF bundles keyed by the raw BEEF array of a `listOutputs`
109
- * result, so token scans parse each bundle once instead of once per output.
110
- * `null` records a bundle that failed to parse. WeakMap-keyed so entries
111
- * are released with their results.
112
- */
113
- parsedBeefCache = new WeakMap();
114
- /** Counts token-field decrypts so long scans can periodically yield. */
115
- tokenFieldDecryptCount = 0;
116
- /** How long a cached permission remains valid (5 minutes). */
117
- static CACHE_TTL_MS = 5 * 60 * 1000;
118
- /** Window during which freshly granted permissions are auto-allowed (except spending). */
119
- static RECENT_GRANT_COVER_MS = 15 * 1000;
120
- /** Default ports used when normalizing originator values. */
121
- static DEFAULT_PORTS = {
122
- 'http:': '80',
123
- 'https:': '443'
124
- };
125
- /**
126
- * Configuration that determines whether to skip or apply various checks and encryption.
127
- */
128
- config;
129
- /**
130
- * Constructs a new Permissions Manager instance.
131
- *
132
- * @param underlyingWallet The underlying BRC-100 wallet, where requests are forwarded after permission is granted
133
- * @param adminOriginator The domain or FQDN that is automatically allowed everything
134
- * @param config A set of boolean flags controlling how strictly permissions are enforced
135
- */
136
- constructor(underlyingWallet, adminOriginator, config = {}) {
137
- this.underlying = underlyingWallet;
138
- this.adminOriginator = this.normalizeOriginator(adminOriginator) || adminOriginator;
139
- // Default all config options to true unless specified
140
- this.config = {
141
- seekProtocolPermissionsForSigning: true,
142
- seekProtocolPermissionsForEncrypting: true,
143
- seekProtocolPermissionsForHMAC: true,
144
- seekPermissionsForKeyLinkageRevelation: true,
145
- seekPermissionsForPublicKeyRevelation: true,
146
- seekPermissionsForIdentityKeyRevelation: true,
147
- seekPermissionsForIdentityResolution: true,
148
- seekBasketInsertionPermissions: true,
149
- seekBasketRemovalPermissions: true,
150
- seekBasketListingPermissions: true,
151
- seekPermissionWhenApplyingActionLabels: true,
152
- seekPermissionWhenListingActionsByLabel: true,
153
- seekCertificateDisclosurePermissions: true,
154
- seekCertificateAcquisitionPermissions: true,
155
- seekCertificateRelinquishmentPermissions: true,
156
- seekCertificateListingPermissions: true,
157
- encryptWalletMetadata: true,
158
- seekSpendingPermissions: true,
159
- seekGroupedPermission: true,
160
- differentiatePrivilegedOperations: true,
161
- whitelistedCounterparties: {},
162
- ...config // override with user-specified config
163
- };
164
- }
165
- /* ---------------------------------------------------------------------
166
- * HELPER METHODS FOR P-MODULE DELEGATION
167
- * --------------------------------------------------------------------- */
168
- /**
169
- * Delegates a wallet method call to a P-module if the basket or protocol name uses a P-scheme.
170
- * Handles the full request/response transformation flow.
171
- *
172
- * @param basketOrProtocolName - The basket or protocol name to check for p-module delegation
173
- * @param method - The wallet method name being called
174
- * @param args - The original args passed to the method
175
- * @param originator - The originator of the request
176
- * @param underlyingCall - Callback that executes the underlying wallet method with transformed args
177
- * @returns The transformed response, or null if not a P-basket/protocol (caller should continue normal flow)
178
- */
179
- async delegateToPModuleIfNeeded(basketOrProtocolName, method, args, originator, underlyingCall) {
180
- // Check if this is a P-protocol/basket
181
- if (!basketOrProtocolName.startsWith('p ')) {
182
- return null; // If not, caller should continue normal flow
183
- }
184
- const schemeID = basketOrProtocolName.split(' ')[1];
185
- const module = this.config.permissionModules?.[schemeID];
186
- if (module == null) {
187
- throw new Error(`Unsupported P-module scheme: p ${schemeID}`);
188
- }
189
- // Transform request with module
190
- const transformedReq = await module.onRequest({
191
- method,
192
- args,
193
- originator
194
- });
195
- // Call underlying method with transformed request
196
- const results = await underlyingCall(transformedReq.args, originator);
197
- // Transform response with module
198
- return await module.onResponse(results, {
199
- method,
200
- originator
201
- });
202
- }
203
- /**
204
- * Adds a permission module for the given schemeID if needed, throwing if unsupported.
205
- */
206
- addPModuleByScheme(schemeID, kind, pModulesByScheme) {
207
- if (pModulesByScheme.has(schemeID))
208
- return;
209
- const module = this.config.permissionModules?.[schemeID];
210
- if (module == null) {
211
- throw new Error(`Unsupported P-${kind} scheme: p ${schemeID}`);
212
- }
213
- pModulesByScheme.set(schemeID, module);
214
- }
215
- /**
216
- * Splits labels into P and non-P lists, registering any P-modules encountered.
217
- *
218
- * P-labels follow BRC-111 format: `p <moduleId> <payload>`
219
- * - Must start with "p " (lowercase p + space)
220
- * - Module ID must be at least 1 character with no spaces
221
- * - Single space separates module ID from payload
222
- * - Payload must be at least 1 character
223
- *
224
- * @example Valid: "p btms token123", "p invoicing invoice 2026-02-02"
225
- * @example Invalid: "p btms" (no payload), "p btms " (empty payload), "p data" (empty moduleId)
226
- *
227
- * @param labels - Array of label strings to process
228
- * @param pModulesByScheme - Map to populate with discovered p-modules
229
- * @returns Array of non-P labels for normal permission checks
230
- * @throws Error if p-label format is invalid or module is unsupported
231
- */
232
- splitLabelsByPermissionModule(labels, pModulesByScheme) {
233
- const nonPLabels = [];
234
- if (labels == null)
235
- return nonPLabels;
236
- for (const label of labels) {
237
- if (label.startsWith('p ')) {
238
- // Remove "p " prefix to get "moduleId payload"
239
- const remainder = label.slice(2);
240
- // Find the space that separates moduleId from payload
241
- const separatorIndex = remainder.indexOf(' ');
242
- // Validate: must have a space (separatorIndex > 0) and payload after it
243
- // separatorIndex <= 0 means no space found or moduleId is empty
244
- // separatorIndex === remainder.length - 1 means space is last char (no payload)
245
- if (separatorIndex <= 0 || separatorIndex === remainder.length - 1) {
246
- throw new Error(`Invalid P-label format: ${label}`);
247
- }
248
- // Reject double spaces after moduleId (payload can't start with space)
249
- if (remainder[separatorIndex + 1] === ' ') {
250
- throw new Error(`Invalid P-label format: ${label}`);
251
- }
252
- // Extract moduleId (substring before first space)
253
- const schemeID = remainder.slice(0, separatorIndex);
254
- // Register the module (throws if unsupported)
255
- this.addPModuleByScheme(schemeID, 'label', pModulesByScheme);
256
- }
257
- else {
258
- // Regular label - add to list for normal permission checks
259
- nonPLabels.push(label);
260
- }
261
- }
262
- return nonPLabels;
263
- }
264
- /**
265
- * Decrypts custom instructions in listOutputs results if encryption is configured.
266
- */
267
- async decryptListOutputsMetadata(results) {
268
- if (results.outputs) {
269
- for (const output of results.outputs) {
270
- if (output.customInstructions) {
271
- output.customInstructions = await this.maybeDecryptMetadata(output.customInstructions);
272
- }
273
- }
274
- }
275
- return results;
276
- }
277
- /**
278
- * Decrypts metadata in listActions results if encryption is configured.
279
- */
280
- async decryptListActionsMetadata(results) {
281
- if (results.actions) {
282
- for (const action of results.actions) {
283
- await this.decryptSingleActionMetadata(action);
284
- }
285
- }
286
- return results;
287
- }
288
- async decryptSingleActionMetadata(action) {
289
- if (action.description) {
290
- action.description = await this.maybeDecryptMetadata(action.description);
291
- }
292
- if (action.inputs != null) {
293
- for (const input of action.inputs) {
294
- if (input.inputDescription) {
295
- input.inputDescription = await this.maybeDecryptMetadata(input.inputDescription);
296
- }
297
- }
298
- }
299
- if (action.outputs != null) {
300
- for (const output of action.outputs) {
301
- await this.decryptActionOutputMetadata(output);
302
- }
303
- }
304
- }
305
- async decryptActionOutputMetadata(output) {
306
- if (output.outputDescription) {
307
- output.outputDescription = await this.maybeDecryptMetadata(output.outputDescription);
308
- }
309
- if (output.customInstructions) {
310
- output.customInstructions = await this.maybeDecryptMetadata(output.customInstructions);
311
- }
312
- }
313
- /* ---------------------------------------------------------------------
314
- * 1) PUBLIC API FOR REGISTERING CALLBACKS (UI PROMPTS, LOGGING, ETC.)
315
- * --------------------------------------------------------------------- */
316
- /**
317
- * Binds a callback function to a named event, such as `onProtocolPermissionRequested`.
318
- *
319
- * @param eventName The name of the event to listen to
320
- * @param handler A function that handles the event
321
- * @returns A numeric ID you can use to unbind later
322
- */
323
- bindCallback(eventName, handler) {
324
- const arr = this.callbacks[eventName];
325
- arr.push(handler);
326
- return arr.length - 1;
327
- }
328
- /**
329
- * Unbinds a previously registered callback by either its numeric ID (returned by `bindCallback`)
330
- * or by exact function reference.
331
- *
332
- * @param eventName The event name, e.g. "onProtocolPermissionRequested"
333
- * @param reference Either the numeric ID or the function reference
334
- * @returns True if successfully unbound, false otherwise
335
- */
336
- unbindCallback(eventName, reference) {
337
- if (this.callbacks[eventName] == null)
338
- return false;
339
- const arr = this.callbacks[eventName];
340
- if (typeof reference === 'number') {
341
- if (arr[reference]) {
342
- arr[reference] = null;
343
- return true;
344
- }
345
- return false;
346
- }
347
- else {
348
- const index = arr.indexOf(reference);
349
- if (index !== -1) {
350
- arr[index] = null;
351
- return true;
352
- }
353
- return false;
354
- }
355
- }
356
- /**
357
- * Internally triggers a named event, calling all subscribed listeners.
358
- * Each callback is awaited in turn (though errors are swallowed so that
359
- * one failing callback doesn't prevent the others).
360
- *
361
- * @param eventName The event name
362
- * @param param The parameter object passed to all listeners
363
- */
364
- async callEvent(eventName, param) {
365
- const arr = this.callbacks[eventName] || [];
366
- for (const cb of arr) {
367
- if (typeof cb === 'function') {
368
- try {
369
- await cb(param);
370
- }
371
- catch (_callbackError) {
372
- // Intentionally swallow errors from user-provided callbacks to prevent
373
- // a misbehaving callback from disrupting the event dispatch loop.
374
- }
375
- }
376
- }
377
- }
378
- /* ---------------------------------------------------------------------
379
- * 2) PERMISSION (GRANT / DENY) METHODS
380
- * --------------------------------------------------------------------- */
381
- /**
382
- * Grants a previously requested permission.
383
- * This method:
384
- * 1) Resolves all pending promise calls waiting on this request
385
- * 2) Optionally creates or renews an on-chain PushDrop token (unless `ephemeral===true`)
386
- *
387
- * @param params requestID to identify which request is granted, plus optional expiry
388
- * or `ephemeral` usage, etc.
389
- */
390
- async grantPermission(params) {
391
- // 1) Identify the matching queued requests in `activeRequests`
392
- const matching = this.activeRequests.get(params.requestID);
393
- if (matching == null) {
394
- throw new Error('Request ID not found.');
395
- }
396
- // 2) Mark all matching requests as resolved, deleting the entry
397
- for (const x of matching.pending) {
398
- x.resolve(true);
399
- }
400
- this.activeRequests.delete(params.requestID);
401
- // 3) If `ephemeral !== true`, we create or renew an on-chain token
402
- // Only cache non-ephemeral permissions
403
- // Ephemeral permissions should not be cached as they are one-time authorizations
404
- if (!params.ephemeral) {
405
- const request = matching.request;
406
- const expiry = params.expiry || 0; // default: never expires
407
- const key = this.buildRequestKey(request);
408
- // Several first-contact prompts for the same permission can stack up
409
- // before any token exists (e.g. one per usage type). Granting them all
410
- // should yield ONE token: if an identical non-renewal grant just minted
411
- // (or is minting), don't mint a duplicate. Spending authorizations are
412
- // excluded — their tokens carry amounts, so every grant is honored.
413
- let skipDuplicateMint = !request.renewal &&
414
- request.type !== 'spending' &&
415
- (this.isPermissionCached(key) || this.isRecentlyGranted(key) || this.mintsInFlight.has(key));
416
- if (skipDuplicateMint) {
417
- const inFlight = this.mintsInFlight.get(key);
418
- if (inFlight != null) {
419
- await inFlight;
420
- // The awaited mint may have failed; only skip if it landed.
421
- skipDuplicateMint = this.isPermissionCached(key) || this.isRecentlyGranted(key);
422
- }
423
- }
424
- if (!skipDuplicateMint) {
425
- const mint = request.renewal
426
- ? this.renewPermissionOnChain(request.previousToken, request, expiry, params.amount)
427
- : this.createPermissionOnChain(request, expiry, params.amount);
428
- // Publish the in-flight mint so concurrent ensures for the same
429
- // permission wait for it instead of re-prompting (stored promise
430
- // never rejects; failures still propagate to this caller below).
431
- this.mintsInFlight.set(key, mint.then(() => { }, () => { }));
432
- try {
433
- await mint;
434
- }
435
- finally {
436
- this.mintsInFlight.delete(key);
437
- }
438
- }
439
- this.cachePermission(key, expiry);
440
- this.markRecentGrant(request);
441
- }
442
- }
443
- /**
444
- * Denies a previously requested permission.
445
- * This method rejects all pending promise calls waiting on that request
446
- *
447
- * @param requestID requestID identifying which request to deny
448
- */
449
- async denyPermission(requestID) {
450
- // 1) Identify the matching requests
451
- const matching = this.activeRequests.get(requestID);
452
- if (matching == null) {
453
- throw new Error('Request ID not found.');
454
- }
455
- // 2) Reject all matching requests, deleting the entry
456
- // Denials carry the canonical machine-readable code so applications can
457
- // distinguish user denial from other failures (matches denyActiveRequest).
458
- const err = new Error('Permission denied.');
459
- err.code = 'ERR_PERMISSION_DENIED';
460
- for (const x of matching.pending) {
461
- x.reject(err);
462
- }
463
- this.activeRequests.delete(requestID);
464
- }
465
- /**
466
- * Grants a previously requested grouped permission.
467
- * @param params.requestID The ID of the request being granted.
468
- * @param params.granted A subset of the originally requested permissions that the user has granted.
469
- * @param params.expiry An optional expiry time (in seconds) for the new permission tokens.
470
- */
471
- async grantGroupedPermission(params) {
472
- const matching = this.activeRequests.get(params.requestID);
473
- if (matching == null) {
474
- throw new Error('Request ID not found.');
475
- }
476
- try {
477
- const originalRequest = matching.request;
478
- const { originator, permissions: requestedPermissions, displayOriginator } = originalRequest;
479
- const originLookupValues = this.buildOriginatorLookupValues(displayOriginator, originator);
480
- this.validateGrantedPermissionsSubset(params.granted, requestedPermissions);
481
- const expiry = params.expiry || 0; // default: never expires
482
- const toCreate = [];
483
- const toRenew = [];
484
- if (params.granted.spendingAuthorization != null) {
485
- toCreate.push({
486
- request: {
487
- type: 'spending',
488
- originator,
489
- spending: { satoshis: params.granted.spendingAuthorization.amount },
490
- reason: params.granted.spendingAuthorization.description
491
- },
492
- expiry: 0,
493
- amount: params.granted.spendingAuthorization.amount
494
- });
495
- }
496
- const grantedProtocols = params.granted.protocolPermissions || [];
497
- const protocolTokens = await this.mapWithConcurrency(grantedProtocols, 8, async (p) => {
498
- const counterparty = (p.protocolID?.[0] ?? 0) === 1 ? '' : p.counterparty || 'self';
499
- const token = await this.findProtocolToken(originator, false, p.protocolID, counterparty, true, originLookupValues);
500
- return { p, token, counterparty };
501
- });
502
- for (const { p, token, counterparty } of protocolTokens) {
503
- const request = {
504
- type: 'protocol',
505
- originator,
506
- privileged: false,
507
- protocolID: p.protocolID,
508
- counterparty,
509
- reason: p.description
510
- };
511
- if (token == null) {
512
- toCreate.push({ request, expiry });
513
- }
514
- else {
515
- toRenew.push({ oldToken: token, request, expiry });
516
- }
517
- }
518
- for (const b of params.granted.basketAccess || []) {
519
- toCreate.push({
520
- request: { type: 'basket', originator, basket: b.basket, reason: b.description },
521
- expiry
522
- });
523
- }
524
- for (const c of params.granted.certificateAccess || []) {
525
- toCreate.push({
526
- request: {
527
- type: 'certificate',
528
- originator,
529
- privileged: false,
530
- certificate: {
531
- verifier: c.verifierPublicKey,
532
- certType: c.type,
533
- fields: c.fields
534
- },
535
- reason: c.description
536
- },
537
- expiry
538
- });
539
- }
540
- const created = await this.createPermissionTokensBestEffort(toCreate);
541
- const renewed = await this.renewPermissionTokensBestEffort(toRenew);
542
- for (const req of [...created, ...renewed]) {
543
- this.markRecentGrant(req);
544
- }
545
- // Success - resolve all pending promises for this request
546
- for (const p of matching.pending) {
547
- p.resolve(true);
548
- }
549
- }
550
- catch (error) {
551
- // Failure - reject all pending promises so callers don't hang forever
552
- for (const p of matching.pending) {
553
- p.reject(error);
554
- }
555
- throw error;
556
- }
557
- finally {
558
- // Always clean up the request entry
559
- this.activeRequests.delete(params.requestID);
560
- }
561
- }
562
- /**
563
- * Denies a previously requested grouped permission.
564
- * @param requestID The ID of the request being denied.
565
- */
566
- /**
567
- * Validates that every entry in `granted` was part of the original `requested` set.
568
- * Throws an error on the first mismatch.
569
- */
570
- validateGrantedPermissionsSubset(granted, requested) {
571
- if ((granted.spendingAuthorization != null) && (requested.spendingAuthorization == null)) {
572
- throw new Error('Granted spending authorization was not part of the original request.');
573
- }
574
- if (granted.protocolPermissions?.some(g => (requested.protocolPermissions?.find(r => deepEqual(r, g))) == null)) {
575
- throw new Error('Granted protocol permissions are not a subset of the original request.');
576
- }
577
- if (granted.basketAccess?.some(g => (requested.basketAccess?.find(r => deepEqual(r, g))) == null)) {
578
- throw new Error('Granted basket access permissions are not a subset of the original request.');
579
- }
580
- if (granted.certificateAccess?.some(g => (requested.certificateAccess?.find(r => deepEqual(r, g))) == null)) {
581
- throw new Error('Granted certificate access permissions are not a subset of the original request.');
582
- }
583
- }
584
- denyActiveRequest(requestID) {
585
- const matching = this.activeRequests.get(requestID);
586
- if (matching == null) {
587
- throw new Error('Request ID not found.');
588
- }
589
- const err = new Error('The user has denied the request for permission.');
590
- err.code = 'ERR_PERMISSION_DENIED';
591
- for (const p of matching.pending) {
592
- p.reject(err);
593
- }
594
- this.activeRequests.delete(requestID);
595
- }
596
- async denyGroupedPermission(requestID) {
597
- this.denyActiveRequest(requestID);
598
- }
599
- async dismissGroupedPermission(requestID) {
600
- const matching = this.activeRequests.get(requestID);
601
- if (matching == null) {
602
- throw new Error('Request ID not found.');
603
- }
604
- for (const p of matching.pending) {
605
- p.resolve(true);
606
- }
607
- this.activeRequests.delete(requestID);
608
- }
609
- async grantCounterpartyPermission(params) {
610
- const matching = this.activeRequests.get(params.requestID);
611
- if (matching == null) {
612
- throw new Error('Request ID not found.');
613
- }
614
- const originalRequest = matching.request;
615
- const { originator, counterparty, permissions: requestedPermissions, displayOriginator } = originalRequest;
616
- const originLookupValues = this.buildOriginatorLookupValues(displayOriginator, originator);
617
- const getProtoName = (p) => {
618
- if (!p)
619
- return undefined;
620
- if (typeof p.protocolName === 'string')
621
- return p.protocolName;
622
- if (Array.isArray(p.protocolID) && typeof p.protocolID[1] === 'string')
623
- return p.protocolID[1];
624
- return undefined;
625
- };
626
- if (params.granted.protocols?.some(g => {
627
- const gName = getProtoName(g);
628
- if (!gName)
629
- return true;
630
- return !requestedPermissions.protocols.some(r => r.protocolName === gName);
631
- })) {
632
- throw new Error('Granted protocol permissions are not a subset of the original request.');
633
- }
634
- const expiry = params.expiry || 0;
635
- const toCreate = [];
636
- const toRenew = [];
637
- const grantedProtocols = params.granted.protocols || [];
638
- const protocolTokens = await this.mapWithConcurrency(grantedProtocols, 8, async (p) => {
639
- const protocolName = getProtoName(p);
640
- if (!protocolName) {
641
- throw new Error('Invalid counterparty permission protocol entry: missing protocolName/protocolID.');
642
- }
643
- const protocolID = [2, protocolName];
644
- const token = await this.findProtocolToken(originator, false, protocolID, counterparty, true, originLookupValues);
645
- return { p, token, protocolID };
646
- });
647
- for (const { p, token, protocolID } of protocolTokens) {
648
- const request = {
649
- type: 'protocol',
650
- originator,
651
- privileged: false,
652
- protocolID,
653
- counterparty,
654
- reason: p.description
655
- };
656
- if (token == null) {
657
- toCreate.push({ request, expiry });
658
- }
659
- else {
660
- toRenew.push({ oldToken: token, request, expiry });
661
- }
662
- }
663
- const created = await this.createPermissionTokensBestEffort(toCreate);
664
- const renewed = await this.renewPermissionTokensBestEffort(toRenew);
665
- for (const req of [...created, ...renewed]) {
666
- this.markRecentGrant(req);
667
- }
668
- for (const p of matching.pending) {
669
- p.resolve(true);
670
- }
671
- this.activeRequests.delete(params.requestID);
672
- }
673
- async denyCounterpartyPermission(requestID) {
674
- this.denyActiveRequest(requestID);
675
- }
676
- /* ---------------------------------------------------------------------
677
- * 3) THE "ENSURE" METHODS: CHECK IF PERMISSION EXISTS, OTHERWISE PROMPT
678
- * --------------------------------------------------------------------- */
679
- /**
680
- * Ensures the originator has protocol usage permission.
681
- * If no valid (unexpired) permission token is found, triggers a permission request flow.
682
- */
683
- async ensureProtocolPermission({ originator, privileged, protocolID, counterparty, reason, seekPermission = true, usageType }) {
684
- const { normalized: normalizedOriginator, lookupValues } = this.prepareOriginator(originator);
685
- originator = normalizedOriginator;
686
- // 1) adminOriginator can do anything
687
- if (this.isAdminOriginator(originator))
688
- return true;
689
- // 2) If security level=0, we consider it “open” usage
690
- const [level, protoName] = protocolID;
691
- if (level === 0)
692
- return true;
693
- if (level === 1)
694
- counterparty = '';
695
- // 3) If protocol is admin-reserved, block
696
- if (this.isAdminProtocol(protocolID)) {
697
- throw new Error(`Protocol “${protoName}” is admin-only.`);
698
- }
699
- // Allow the configured exceptions.
700
- if (this.isProtocolUsageTypeExempted(usageType))
701
- return true;
702
- if (!this.config.differentiatePrivilegedOperations)
703
- privileged = false;
704
- if (!privileged && this.isWhitelistedCounterpartyProtocol(counterparty, protocolID))
705
- return true;
706
- const cacheKey = this.buildRequestKey({ type: 'protocol', originator, privileged, protocolID, counterparty });
707
- if (await this.hasRecentOrPendingGrant(cacheKey))
708
- return true;
709
- // 4) Attempt to find a valid token in the internal basket
710
- const token = await this.findProtocolToken(originator, privileged, protocolID, counterparty, /* includeExpired= */ true, lookupValues);
711
- if (token == null) {
712
- // No token found => request a new one if allowed
713
- if (!seekPermission)
714
- throw new Error('No protocol permission token found (seekPermission=false).');
715
- return await this.requestPermissionFlow({
716
- type: 'protocol', originator, privileged, protocolID, counterparty, usageType, reason, renewal: false
717
- });
718
- }
719
- if (this.isTokenExpired(token.expiry)) {
720
- // has a token but expired => request renewal if allowed
721
- if (!seekPermission) {
722
- throw new Error('Protocol permission expired and no further user consent allowed (seekPermission=false).');
723
- }
724
- return await this.requestPermissionFlow({
725
- type: 'protocol', originator, privileged, protocolID, counterparty, usageType, reason,
726
- renewal: true, previousToken: token
727
- });
728
- }
729
- // valid and unexpired
730
- this.cachePermission(cacheKey, token.expiry);
731
- return true;
732
- }
733
- /**
734
- * Ensures the originator has basket usage permission for the specified basket.
735
- * If not, triggers a permission request flow.
736
- */
737
- async ensureBasketAccess({ originator, basket, reason, seekPermission = true, usageType }) {
738
- const { normalized: normalizedOriginator, lookupValues } = this.prepareOriginator(originator);
739
- originator = normalizedOriginator;
740
- if (this.isAdminOriginator(originator))
741
- return true;
742
- if (this.isAdminBasket(basket))
743
- throw new Error(`Basket “${basket}” is admin-only.`);
744
- if (!this.isBasketUsageRequired(usageType))
745
- return true;
746
- const cacheKey = this.buildRequestKey({ type: 'basket', originator, basket });
747
- if (await this.hasRecentOrPendingGrant(cacheKey))
748
- return true;
749
- const token = await this.findBasketToken(originator, basket, true, lookupValues);
750
- if (token == null) {
751
- if (!seekPermission)
752
- throw new Error('No basket permission found, and no user consent allowed (seekPermission=false).');
753
- return await this.requestPermissionFlow({ type: 'basket', originator, basket, usageType, reason, renewal: false });
754
- }
755
- if (this.isTokenExpired(token.expiry)) {
756
- if (!seekPermission)
757
- throw new Error('Basket permission expired (seekPermission=false).');
758
- return await this.requestPermissionFlow({
759
- type: 'basket', originator, basket, usageType, reason, renewal: true, previousToken: token
760
- });
761
- }
762
- this.cachePermission(cacheKey, token.expiry);
763
- return true;
764
- }
765
- /** Returns false when the basket usageType does NOT need a permission check (config-gated). */
766
- isBasketUsageRequired(usageType) {
767
- if (usageType === 'insertion' && !this.config.seekBasketInsertionPermissions)
768
- return false;
769
- if (usageType === 'removal' && !this.config.seekBasketRemovalPermissions)
770
- return false;
771
- if (usageType === 'listing' && !this.config.seekBasketListingPermissions)
772
- return false;
773
- return true;
774
- }
775
- /**
776
- * Ensures the originator has a valid certificate permission.
777
- * This is relevant when revealing certificate fields in DCAP contexts.
778
- */
779
- async ensureCertificateAccess({ originator, privileged, verifier, certType, fields, reason, seekPermission = true, usageType }) {
780
- const { normalized: normalizedOriginator, lookupValues } = this.prepareOriginator(originator);
781
- originator = normalizedOriginator;
782
- if (this.isAdminOriginator(originator))
783
- return true;
784
- if (usageType === 'disclosure' && !this.config.seekCertificateDisclosurePermissions) {
785
- return true;
786
- }
787
- if (!this.config.differentiatePrivilegedOperations) {
788
- privileged = false;
789
- }
790
- const cacheKey = this.buildRequestKey({
791
- type: 'certificate',
792
- originator,
793
- privileged,
794
- certificate: { verifier, certType, fields }
795
- });
796
- if (await this.hasRecentOrPendingGrant(cacheKey)) {
797
- return true;
798
- }
799
- const token = await this.findCertificateToken(originator, privileged, verifier, certType, fields,
800
- /* includeExpired= */ true, lookupValues);
801
- if (token == null) {
802
- if (!seekPermission) {
803
- throw new Error('No certificate permission found (seekPermission=false).');
804
- }
805
- return await this.requestPermissionFlow({
806
- type: 'certificate',
807
- originator,
808
- privileged,
809
- certificate: { verifier, certType, fields },
810
- usageType,
811
- reason,
812
- renewal: false
813
- });
814
- }
815
- if (this.isTokenExpired(token.expiry)) {
816
- if (!seekPermission) {
817
- throw new Error('Certificate permission expired (seekPermission=false).');
818
- }
819
- return await this.requestPermissionFlow({
820
- type: 'certificate',
821
- originator,
822
- privileged,
823
- certificate: { verifier, certType, fields },
824
- usageType,
825
- reason,
826
- renewal: true,
827
- previousToken: token
828
- });
829
- }
830
- this.cachePermission(cacheKey, token.expiry);
831
- return true;
832
- }
833
- /**
834
- * Ensures the originator has spending authorization (DSAP) for a certain satoshi amount.
835
- * If the existing token limit is insufficient, attempts to renew. If no token, attempts to create one.
836
- */
837
- async ensureSpendingAuthorization({ originator, satoshis, lineItems, reason, seekPermission = true }) {
838
- const { normalized: normalizedOriginator, lookupValues } = this.prepareOriginator(originator);
839
- originator = normalizedOriginator;
840
- if (this.isAdminOriginator(originator))
841
- return true;
842
- if (!this.config.seekSpendingPermissions) {
843
- // We skip spending permission entirely
844
- return true;
845
- }
846
- const cacheKey = this.buildRequestKey({ type: 'spending', originator, spending: { satoshis } });
847
- // Spending keys are amount-scoped. The recent-grant window this adds sits
848
- // inside the pre-existing permissionCache window grantPermission already
849
- // wrote for spending, so accounting exposure is unchanged.
850
- if (await this.hasRecentOrPendingGrant(cacheKey)) {
851
- return true;
852
- }
853
- const token = await this.findSpendingToken(originator, lookupValues);
854
- if (token?.authorizedAmount) {
855
- // Check how much has been spent so far
856
- const spentSoFar = await this.querySpentSince(token);
857
- if (spentSoFar + satoshis <= token.authorizedAmount) {
858
- this.cachePermission(cacheKey, token.expiry);
859
- return true;
860
- }
861
- else {
862
- // Renew if possible
863
- if (!seekPermission) {
864
- throw new Error(`Spending authorization insufficient for ${satoshis}, no user consent (seekPermission=false).`);
865
- }
866
- return await this.requestPermissionFlow({
867
- type: 'spending',
868
- originator,
869
- spending: { satoshis, lineItems },
870
- reason,
871
- renewal: true,
872
- previousToken: token
873
- });
874
- }
875
- }
876
- else {
877
- // no token
878
- if (!seekPermission) {
879
- throw new Error('No spending authorization found, (seekPermission=false).');
880
- }
881
- return await this.requestPermissionFlow({
882
- type: 'spending',
883
- originator,
884
- spending: { satoshis, lineItems },
885
- reason,
886
- renewal: false
887
- });
888
- }
889
- }
890
- /**
891
- * Ensures the originator has label usage permission.
892
- * If no valid (unexpired) permission token is found, triggers a permission request flow.
893
- */
894
- async ensureLabelAccess({ originator, label, reason, seekPermission = true, usageType }) {
895
- const { normalized: normalizedOriginator } = this.prepareOriginator(originator);
896
- originator = normalizedOriginator;
897
- // 1) adminOriginator can do anything
898
- if (this.isAdminOriginator(originator))
899
- return true;
900
- // 2) If label is admin-reserved, block
901
- if (this.isAdminLabel(label)) {
902
- throw new Error(`Label “${label}” is admin-only.`);
903
- }
904
- if (usageType === 'apply' && !this.config.seekPermissionWhenApplyingActionLabels) {
905
- return true;
906
- }
907
- if (usageType === 'list' && !this.config.seekPermissionWhenListingActionsByLabel) {
908
- return true;
909
- }
910
- const cacheKey = this.buildRequestKey({
911
- type: 'protocol',
912
- originator,
913
- privileged: false,
914
- protocolID: [1, `action label ${label}`],
915
- counterparty: 'self'
916
- });
917
- if (this.isPermissionCached(cacheKey)) {
918
- return true;
919
- }
920
- // 3) Let ensureProtocolPermission handle the rest.
921
- return await this.ensureProtocolPermission({
922
- originator,
923
- privileged: false,
924
- protocolID: [1, `action label ${label}`],
925
- counterparty: 'self',
926
- reason,
927
- seekPermission,
928
- usageType: 'generic'
929
- });
930
- }
931
- /**
932
- * Returns true when the given usageType is configured to skip permission checks.
933
- */
934
- isProtocolUsageTypeExempted(usageType) {
935
- if (usageType === 'signing' && !this.config.seekProtocolPermissionsForSigning)
936
- return true;
937
- if (usageType === 'encrypting' && !this.config.seekProtocolPermissionsForEncrypting)
938
- return true;
939
- if (usageType === 'hmac' && !this.config.seekProtocolPermissionsForHMAC)
940
- return true;
941
- if (usageType === 'publicKey' && !this.config.seekPermissionsForPublicKeyRevelation)
942
- return true;
943
- if (usageType === 'identityKey' && !this.config.seekPermissionsForIdentityKeyRevelation)
944
- return true;
945
- if (usageType === 'linkageRevelation' && !this.config.seekPermissionsForKeyLinkageRevelation)
946
- return true;
947
- return false;
948
- }
949
- isProtocolInCounterpartyPermissions(protocolID, counterpartyPermissions) {
950
- const [, name] = protocolID;
951
- return counterpartyPermissions.protocols.some(p => p.protocolName === name);
952
- }
953
- validateCounterpartyPermissions(raw) {
954
- if (!raw || !Array.isArray(raw.protocols) || raw.protocols.length === 0)
955
- return null;
956
- const getCI = (obj, key) => {
957
- if (!obj || typeof obj !== 'object')
958
- return undefined;
959
- const lower = key.toLowerCase();
960
- const found = Object.keys(obj).find(k => k.toLowerCase() === lower);
961
- return found ? obj[found] : undefined;
962
- };
963
- const parseProtocolId = (v) => {
964
- if (!Array.isArray(v))
965
- return null;
966
- if (v[0] !== 2)
967
- return null;
968
- if (typeof v[1] !== 'string')
969
- return null;
970
- return v[1];
971
- };
972
- const validProtocols = raw.protocols
973
- .map((p) => {
974
- const protocolName = getCI(p, 'protocolName');
975
- if (typeof protocolName === 'string') {
976
- return {
977
- protocolName,
978
- protocolID: [2, protocolName],
979
- description: typeof p?.description === 'string' ? p.description : undefined
980
- };
981
- }
982
- const protocolIdRaw = getCI(p, 'protocolID') ?? getCI(p, 'protocolId') ?? getCI(p, 'protocolid');
983
- const parsed = parseProtocolId(protocolIdRaw);
984
- if (!parsed)
985
- return null;
986
- return {
987
- protocolName: parsed,
988
- protocolID: [2, parsed],
989
- description: typeof p?.description === 'string' ? p.description : undefined
990
- };
991
- })
992
- .filter(Boolean);
993
- if (validProtocols.length === 0)
994
- return null;
995
- return {
996
- description: typeof raw.description === 'string' ? raw.description : undefined,
997
- protocols: validProtocols
998
- };
999
- }
1000
- async fetchManifestPermissions(originator) {
1001
- const cached = this.manifestCache.get(originator);
1002
- if ((cached != null) && Date.now() - cached.fetchedAt < WalletPermissionsManager.MANIFEST_CACHE_TTL_MS) {
1003
- return {
1004
- groupPermissions: cached.groupPermissions,
1005
- counterpartyPermissions: cached.counterpartyPermissions
1006
- };
1007
- }
1008
- const inProgress = this.manifestFetchInProgress.get(originator);
1009
- if (inProgress != null) {
1010
- return await inProgress;
1011
- }
1012
- const fetchPromise = (async () => {
1013
- try {
1014
- const proto = originator.startsWith('localhost:') ? 'http' : 'https';
1015
- const response = await fetch(`${proto}://${originator}/manifest.json`);
1016
- if (response.ok) {
1017
- const manifest = await response.json();
1018
- const namespace = manifest?.metanet || manifest?.babbage;
1019
- const groupPermissions = namespace?.groupPermissions || null;
1020
- const counterpartyPermissionsDeclared = this.validateCounterpartyPermissions(namespace?.counterpartyPermissions);
1021
- this.manifestCache.set(originator, {
1022
- groupPermissions,
1023
- counterpartyPermissions: counterpartyPermissionsDeclared,
1024
- fetchedAt: Date.now()
1025
- });
1026
- return { groupPermissions, counterpartyPermissions: counterpartyPermissionsDeclared };
1027
- }
1028
- }
1029
- catch (_manifestFetchError) {
1030
- // Manifest fetch or parse failed — fall through to return null/null defaults below
1031
- }
1032
- const result = { groupPermissions: null, counterpartyPermissions: null };
1033
- this.manifestCache.set(originator, { ...result, fetchedAt: Date.now() });
1034
- return result;
1035
- })();
1036
- this.manifestFetchInProgress.set(originator, fetchPromise);
1037
- try {
1038
- return await fetchPromise;
1039
- }
1040
- finally {
1041
- this.manifestFetchInProgress.delete(originator);
1042
- }
1043
- }
1044
- async fetchManifestGroupPermissions(originator) {
1045
- const { groupPermissions } = await this.fetchManifestPermissions(originator);
1046
- return groupPermissions;
1047
- }
1048
- async filterAlreadyGrantedPermissions(originator, groupPermissions) {
1049
- const permissionsToRequest = {
1050
- description: groupPermissions.description,
1051
- protocolPermissions: [],
1052
- basketAccess: [],
1053
- certificateAccess: []
1054
- };
1055
- const [spendingAuthorization, protocolPermissions, basketAccess, certificateAccess] = await Promise.all([
1056
- (async () => {
1057
- if (groupPermissions.spendingAuthorization == null)
1058
- return undefined;
1059
- // A standing authorization is a grant regardless of how much of the
1060
- // monthly allowance has been used — exhaustion is handled by renewal
1061
- // at spend time. Checking remaining headroom here re-requests (and,
1062
- // if approved, re-mints) spending after any monthly usage.
1063
- const { normalized, lookupValues } = this.prepareOriginator(originator);
1064
- const token = await this.findSpendingToken(normalized, lookupValues);
1065
- return token != null ? undefined : groupPermissions.spendingAuthorization;
1066
- })(),
1067
- (async () => {
1068
- const protocolChecks = await Promise.all((groupPermissions.protocolPermissions || []).map(async (p) => {
1069
- if (p.counterparty === '')
1070
- return null;
1071
- const [level] = p.protocolID || [0];
1072
- if (level === 2 && (p.counterparty === undefined || p.counterparty === null)) {
1073
- return null;
1074
- }
1075
- const hasPerm = await this.hasProtocolPermission({
1076
- originator,
1077
- privileged: false,
1078
- protocolID: p.protocolID,
1079
- counterparty: p.counterparty ?? 'self'
1080
- });
1081
- return hasPerm ? null : p;
1082
- }));
1083
- return protocolChecks.filter(Boolean);
1084
- })(),
1085
- (async () => {
1086
- const basketChecks = await Promise.all((groupPermissions.basketAccess || []).map(async (b) => {
1087
- const hasAccess = await this.hasBasketAccess({
1088
- originator,
1089
- basket: b.basket
1090
- });
1091
- return hasAccess ? null : b;
1092
- }));
1093
- return basketChecks.filter(Boolean);
1094
- })(),
1095
- (async () => {
1096
- const certChecks = await Promise.all((groupPermissions.certificateAccess || []).map(async (c) => {
1097
- const hasAccess = await this.hasCertificateAccess({
1098
- originator,
1099
- privileged: false,
1100
- verifier: c.verifierPublicKey,
1101
- certType: c.type,
1102
- fields: c.fields
1103
- });
1104
- return hasAccess ? null : c;
1105
- }));
1106
- return certChecks.filter(Boolean);
1107
- })()
1108
- ]);
1109
- if (spendingAuthorization != null) {
1110
- permissionsToRequest.spendingAuthorization = spendingAuthorization;
1111
- }
1112
- permissionsToRequest.protocolPermissions = protocolPermissions;
1113
- permissionsToRequest.basketAccess = basketAccess;
1114
- permissionsToRequest.certificateAccess = certificateAccess;
1115
- return permissionsToRequest;
1116
- }
1117
- hasAnyPermissionsToRequest(permissions) {
1118
- return !!(permissions.spendingAuthorization ||
1119
- (permissions.protocolPermissions?.length ?? 0) > 0 ||
1120
- (permissions.basketAccess?.length ?? 0) > 0 ||
1121
- (permissions.certificateAccess?.length ?? 0) > 0);
1122
- }
1123
- hasGroupedPermissionRequestedHandlers() {
1124
- const handlers = this.callbacks.onGroupedPermissionRequested || [];
1125
- return handlers.some(h => typeof h === 'function');
1126
- }
1127
- hasCounterpartyPermissionRequestedHandlers() {
1128
- const handlers = this.callbacks.onCounterpartyPermissionRequested || [];
1129
- return handlers.some(h => typeof h === 'function');
1130
- }
1131
- async hasPactEstablished(originator, counterparty) {
1132
- if (counterparty === 'self' || counterparty === 'anyone') {
1133
- return true;
1134
- }
1135
- const cacheKey = `${originator}:${counterparty}`;
1136
- if (this.pactEstablishedCache.has(cacheKey)) {
1137
- return true;
1138
- }
1139
- const { counterpartyPermissions } = await this.fetchManifestPermissions(originator);
1140
- if (!counterpartyPermissions?.protocols?.length) {
1141
- return true;
1142
- }
1143
- const firstProtocol = counterpartyPermissions.protocols[0];
1144
- const hasToken = await this.hasProtocolPermission({
1145
- originator,
1146
- privileged: false,
1147
- protocolID: [2, firstProtocol.protocolName],
1148
- counterparty
1149
- });
1150
- if (hasToken) {
1151
- this.pactEstablishedCache.set(cacheKey, Date.now());
1152
- return true;
1153
- }
1154
- return false;
1155
- }
1156
- markPactEstablished(originator, counterparty) {
1157
- const cacheKey = `${originator}:${counterparty}`;
1158
- this.pactEstablishedCache.set(cacheKey, Date.now());
1159
- }
1160
- async maybeRequestPact(currentRequest) {
1161
- if (!this.config.seekGroupedPermission) {
1162
- return null;
1163
- }
1164
- if (!this.hasCounterpartyPermissionRequestedHandlers()) {
1165
- return null;
1166
- }
1167
- if (currentRequest.type !== 'protocol') {
1168
- return null;
1169
- }
1170
- if (currentRequest.privileged) {
1171
- return null;
1172
- }
1173
- const [level] = currentRequest.protocolID;
1174
- if (level !== 2) {
1175
- return null;
1176
- }
1177
- const originator = currentRequest.originator;
1178
- const counterparty = currentRequest.counterparty;
1179
- if (!counterparty || counterparty === 'self' || counterparty === 'anyone') {
1180
- return null;
1181
- }
1182
- if (!/^[0-9a-fA-F]{66}$/.test(counterparty)) {
1183
- return null;
1184
- }
1185
- const { counterpartyPermissions } = await this.fetchManifestPermissions(originator);
1186
- if (!counterpartyPermissions?.protocols?.length) {
1187
- return null;
1188
- }
1189
- if (!this.isProtocolInCounterpartyPermissions(currentRequest.protocolID, counterpartyPermissions)) {
1190
- return null;
1191
- }
1192
- if (await this.hasPactEstablished(originator, counterparty)) {
1193
- return null;
1194
- }
1195
- const protocolChecks = await Promise.all(counterpartyPermissions.protocols.map(async (p) => {
1196
- const hasPerm = await this.hasProtocolPermission({
1197
- originator,
1198
- privileged: false,
1199
- protocolID: [2, p.protocolName],
1200
- counterparty
1201
- });
1202
- return hasPerm ? null : p;
1203
- }));
1204
- const protocolsToRequest = protocolChecks.filter(Boolean);
1205
- if (protocolsToRequest.length === 0) {
1206
- this.markPactEstablished(originator, counterparty);
1207
- return null;
1208
- }
1209
- const permissionsToRequest = {
1210
- description: counterpartyPermissions.description,
1211
- protocols: protocolsToRequest
1212
- };
1213
- const key = `pact:${originator}:${counterparty}`;
1214
- await this.joinOrCreatePactRequest(key, originator, counterparty, permissionsToRequest, currentRequest.displayOriginator);
1215
- this.markPactEstablished(originator, counterparty);
1216
- const satisfied = await this.hasProtocolPermission({
1217
- originator,
1218
- privileged: false,
1219
- protocolID: currentRequest.protocolID,
1220
- counterparty
1221
- });
1222
- return satisfied ? true : null;
1223
- }
1224
- async joinOrCreatePactRequest(key, originator, counterparty, permissionsToRequest, displayOriginator) {
1225
- const existing = this.activeRequests.get(key);
1226
- if (existing == null) {
1227
- const counterpartyPromise = new Promise((resolve, reject) => {
1228
- this.activeRequests.set(key, {
1229
- request: { originator, counterparty, permissions: permissionsToRequest, displayOriginator },
1230
- pending: [{ resolve, reject }]
1231
- });
1232
- });
1233
- await this.callEvent('onCounterpartyPermissionRequested', {
1234
- requestID: key,
1235
- originator,
1236
- counterparty,
1237
- permissions: permissionsToRequest
1238
- });
1239
- await counterpartyPromise;
1240
- }
1241
- else {
1242
- const existingRequest = existing.request;
1243
- for (const p of permissionsToRequest.protocols) {
1244
- if (!existingRequest.permissions.protocols.some(x => x.protocolName === p.protocolName)) {
1245
- existingRequest.permissions.protocols.push(p);
1246
- }
1247
- }
1248
- await new Promise((resolve, reject) => {
1249
- existing.pending.push({ resolve, reject });
1250
- });
1251
- }
1252
- }
1253
- async maybeRequestPeerGroupedLevel2ProtocolPermissions(currentRequest) {
1254
- if (!this.config.seekGroupedPermission) {
1255
- return null;
1256
- }
1257
- if (!this.hasGroupedPermissionRequestedHandlers()) {
1258
- return null;
1259
- }
1260
- if (currentRequest.type !== 'protocol') {
1261
- return null;
1262
- }
1263
- const [level] = currentRequest.protocolID;
1264
- if (level !== 2) {
1265
- return null;
1266
- }
1267
- const originator = currentRequest.originator;
1268
- const privileged = currentRequest.privileged ?? false;
1269
- const counterparty = currentRequest.counterparty ?? 'self';
1270
- const groupPermissions = await this.fetchManifestGroupPermissions(originator);
1271
- if (groupPermissions == null) {
1272
- return null;
1273
- }
1274
- const normalizeManifestCounterparty = (cp) => {
1275
- if (cp === '')
1276
- return '';
1277
- if (cp === undefined || cp === null)
1278
- return counterparty;
1279
- return cp;
1280
- };
1281
- const manifestLevel2ForThisPeer = (groupPermissions.protocolPermissions || [])
1282
- .filter(p => (p.protocolID?.[0] ?? 0) === 2)
1283
- .map(p => ({
1284
- protocolID: p.protocolID,
1285
- counterparty: normalizeManifestCounterparty(p.counterparty),
1286
- description: p.description
1287
- }))
1288
- .filter(p => p.counterparty !== '')
1289
- .filter(p => p.counterparty === counterparty);
1290
- const isCurrentRequestInManifest = manifestLevel2ForThisPeer.some(p => deepEqual(p.protocolID, currentRequest.protocolID));
1291
- if (!isCurrentRequestInManifest) {
1292
- return null;
1293
- }
1294
- const permissionsToRequest = {
1295
- protocolPermissions: []
1296
- };
1297
- const protocolChecks = await Promise.all(manifestLevel2ForThisPeer.map(async (p) => {
1298
- const hasPerm = await this.hasProtocolPermission({
1299
- originator,
1300
- privileged,
1301
- protocolID: p.protocolID,
1302
- counterparty: p.counterparty
1303
- });
1304
- return hasPerm
1305
- ? null
1306
- : {
1307
- protocolID: p.protocolID,
1308
- counterparty: p.counterparty,
1309
- description: p.description
1310
- };
1311
- }));
1312
- permissionsToRequest.protocolPermissions = protocolChecks.filter(Boolean);
1313
- if (!this.hasAnyPermissionsToRequest(permissionsToRequest)) {
1314
- return null;
1315
- }
1316
- const key = `group-peer:${originator}:${privileged}:${counterparty}`;
1317
- await this.joinOrCreateGroupedRequest(key, originator, permissionsToRequest, currentRequest.displayOriginator);
1318
- const satisfied = await this.checkSpecificPermissionAfterGroupFlow(currentRequest);
1319
- return satisfied ? true : null;
1320
- }
1321
- async withGroupedPermissionFlowLock(originator, fn) {
1322
- const priorTail = this.groupedPermissionFlowTail.get(originator) || Promise.resolve();
1323
- const safePriorTail = priorTail.catch(() => { });
1324
- let release;
1325
- const gate = new Promise(resolve => {
1326
- release = resolve;
1327
- });
1328
- const currentTail = safePriorTail.then(async () => await gate);
1329
- this.groupedPermissionFlowTail.set(originator, currentTail);
1330
- await safePriorTail;
1331
- try {
1332
- return await fn();
1333
- }
1334
- finally {
1335
- release?.();
1336
- currentTail.finally(() => {
1337
- if (this.groupedPermissionFlowTail.get(originator) === currentTail) {
1338
- this.groupedPermissionFlowTail.delete(originator);
1339
- }
1340
- });
1341
- }
1342
- }
1343
- async checkSpecificPermissionAfterGroupFlow(request) {
1344
- switch (request.type) {
1345
- case 'protocol':
1346
- return await this.hasProtocolPermission({
1347
- originator: request.originator,
1348
- privileged: request.privileged ?? false,
1349
- protocolID: request.protocolID,
1350
- counterparty: request.counterparty ?? 'self'
1351
- });
1352
- case 'basket':
1353
- return await this.hasBasketAccess({
1354
- originator: request.originator,
1355
- basket: request.basket
1356
- });
1357
- case 'certificate':
1358
- return await this.hasCertificateAccess({
1359
- originator: request.originator,
1360
- privileged: request.privileged ?? false,
1361
- verifier: request.certificate.verifier,
1362
- certType: request.certificate.certType,
1363
- fields: request.certificate.fields
1364
- });
1365
- case 'spending':
1366
- return await this.hasSpendingAuthorization({
1367
- originator: request.originator,
1368
- satoshis: request.spending.satoshis
1369
- });
1370
- default:
1371
- return false;
1372
- }
1373
- }
1374
- isRequestIncludedInGroupPermissions(request, groupPermissions) {
1375
- switch (request.type) {
1376
- case 'protocol': {
1377
- if (request.privileged)
1378
- return false;
1379
- const pid = request.protocolID;
1380
- if (pid == null)
1381
- return false;
1382
- const cp = request.counterparty ?? 'self';
1383
- return !!groupPermissions.protocolPermissions?.some(p => {
1384
- if (p.counterparty === '')
1385
- return false;
1386
- const [level] = p.protocolID || [0];
1387
- if (level === 2 && (p.counterparty === undefined || p.counterparty === null)) {
1388
- return false;
1389
- }
1390
- const manifestCp = level === 1 ? '' : (p.counterparty ?? 'self');
1391
- return deepEqual(p.protocolID, pid) && manifestCp === cp;
1392
- });
1393
- }
1394
- case 'basket': {
1395
- const basket = request.basket;
1396
- if (!basket)
1397
- return false;
1398
- return !!groupPermissions.basketAccess?.some(b => b.basket === basket);
1399
- }
1400
- case 'certificate': {
1401
- if (request.privileged)
1402
- return false;
1403
- const cert = request.certificate;
1404
- if (cert == null)
1405
- return false;
1406
- return !!groupPermissions.certificateAccess?.some(c => {
1407
- const fieldsA = new Set(c.fields || []);
1408
- const fieldsB = new Set(cert.fields || []);
1409
- if (fieldsA.size !== fieldsB.size)
1410
- return false;
1411
- for (const f of fieldsA)
1412
- if (!fieldsB.has(f))
1413
- return false;
1414
- return c.type === cert.certType && c.verifierPublicKey === cert.verifier;
1415
- });
1416
- }
1417
- case 'spending':
1418
- return groupPermissions.spendingAuthorization != null;
1419
- default:
1420
- return false;
1421
- }
1422
- }
1423
- async maybeRequestGroupedPermissions(currentRequest) {
1424
- if (!this.config.seekGroupedPermission) {
1425
- return null;
1426
- }
1427
- const originator = currentRequest.originator;
1428
- const groupPermissions = await this.fetchManifestGroupPermissions(originator);
1429
- if (groupPermissions == null) {
1430
- return null;
1431
- }
1432
- if (!this.isRequestIncludedInGroupPermissions(currentRequest, groupPermissions)) {
1433
- return null;
1434
- }
1435
- const permissionsToRequest = await this.filterAlreadyGrantedPermissions(originator, groupPermissions);
1436
- if (!this.hasAnyPermissionsToRequest(permissionsToRequest)) {
1437
- return null;
1438
- }
1439
- const key = `group:${originator}`;
1440
- await this.joinOrCreateGroupedRequest(key, originator, permissionsToRequest, currentRequest.displayOriginator);
1441
- const satisfied = await this.checkSpecificPermissionAfterGroupFlow(currentRequest);
1442
- return satisfied ? true : null;
1443
- }
1444
- /**
1445
- * Joins an existing grouped permission request (piggybacks on the existing promise), or creates
1446
- * a new one and fires the onGroupedPermissionRequested event.
1447
- */
1448
- async joinOrCreateGroupedRequest(key, originator, permissionsToRequest, displayOriginator) {
1449
- const existing = this.activeRequests.get(key);
1450
- if (existing == null) {
1451
- const permPromise = new Promise((resolve, reject) => {
1452
- this.activeRequests.set(key, {
1453
- request: { originator, permissions: permissionsToRequest, displayOriginator },
1454
- pending: [{ resolve, reject }]
1455
- });
1456
- });
1457
- await this.callEvent('onGroupedPermissionRequested', {
1458
- requestID: key,
1459
- originator,
1460
- permissions: permissionsToRequest
1461
- });
1462
- await permPromise;
1463
- }
1464
- else {
1465
- const existingRequest = existing.request;
1466
- existingRequest.permissions.protocolPermissions ??= [];
1467
- for (const p of permissionsToRequest.protocolPermissions || []) {
1468
- if (!existingRequest.permissions.protocolPermissions.some(x => deepEqual(x, p))) {
1469
- existingRequest.permissions.protocolPermissions.push(p);
1470
- }
1471
- }
1472
- await new Promise((resolve, reject) => {
1473
- existing.pending.push({ resolve, reject });
1474
- });
1475
- }
1476
- }
1477
- /**
1478
- * A central method that triggers the permission request flow.
1479
- * - It checks if there's already an active request for the same key
1480
- * - If so, we wait on that existing request rather than creating a duplicative one
1481
- * - Otherwise we create a new request queue, call the relevant "onXXXRequested" event,
1482
- * and return a promise that resolves once permission is granted or rejects if denied.
1483
- */
1484
- async requestPermissionFlow(r) {
1485
- const normalizedOriginator = this.normalizeOriginator(r.originator) || r.originator;
1486
- const preparedRequest = {
1487
- ...r,
1488
- originator: normalizedOriginator,
1489
- displayOriginator: r.displayOriginator ?? r.previousToken?.rawOriginator ?? r.originator
1490
- };
1491
- if (preparedRequest.type === 'protocol' && preparedRequest.protocolID?.[0] === 1) {
1492
- preparedRequest.counterparty = '';
1493
- }
1494
- const key = this.buildActiveRequestKey(preparedRequest);
1495
- // If there's already a queue for the same resource, we piggyback on it
1496
- if (this.isPiggybacking(key)) {
1497
- return await this.piggybackOnExistingQueue(key);
1498
- }
1499
- const pactResult = await this.maybeRequestPact(preparedRequest);
1500
- if (pactResult !== null)
1501
- return pactResult;
1502
- const peerGroupResult = await this.maybeRequestPeerGroupedLevel2ProtocolPermissions(preparedRequest);
1503
- if (peerGroupResult !== null)
1504
- return peerGroupResult;
1505
- const groupResult = await this.resolveGroupedFlow(preparedRequest, key);
1506
- if (groupResult !== null)
1507
- return groupResult;
1508
- // Re-check after grouped flow — another waiter may have set up a queue
1509
- if (this.isPiggybacking(key))
1510
- return await this.piggybackOnExistingQueue(key);
1511
- return await this.enqueueAndFireEvent(preparedRequest, key);
1512
- }
1513
- /** Returns true when an active-request queue already has pending waiters for this key. */
1514
- isPiggybacking(key) {
1515
- const q = this.activeRequests.get(key);
1516
- return (q != null) && q.pending.length > 0;
1517
- }
1518
- /** Appends to an existing request queue and waits for resolution. */
1519
- async piggybackOnExistingQueue(key) {
1520
- const q = this.activeRequests.get(key);
1521
- return await new Promise((resolve, reject) => {
1522
- q.pending.push({ resolve, reject });
1523
- });
1524
- }
1525
- /** Runs the grouped-permission flow (with or without a lock) and checks post-group satisfaction. */
1526
- async resolveGroupedFlow(preparedRequest, key) {
1527
- const hadPendingGroupedFlowBefore = this.config.seekGroupedPermission && this.groupedPermissionFlowTail.has(preparedRequest.originator);
1528
- let groupResult;
1529
- if (this.config.seekGroupedPermission) {
1530
- groupResult = await this.withGroupedPermissionFlowLock(preparedRequest.originator, async () => {
1531
- return await this.maybeRequestGroupedPermissions(preparedRequest);
1532
- });
1533
- }
1534
- else {
1535
- groupResult = await this.maybeRequestGroupedPermissions(preparedRequest);
1536
- }
1537
- if (groupResult !== null)
1538
- return groupResult;
1539
- if (hadPendingGroupedFlowBefore) {
1540
- const satisfiedAfterGroup = await this.checkSpecificPermissionAfterGroupFlow(preparedRequest);
1541
- if (satisfiedAfterGroup)
1542
- return true;
1543
- }
1544
- return null;
1545
- }
1546
- /** Creates a new active-request queue entry, fires the event, and returns the promise. */
1547
- async enqueueAndFireEvent(preparedRequest, key) {
1548
- const requestPromise = new Promise((resolve, reject) => {
1549
- this.activeRequests.set(key, { request: preparedRequest, pending: [{ resolve, reject }] });
1550
- });
1551
- try {
1552
- await this.firePermissionRequestEvent(preparedRequest, key);
1553
- }
1554
- catch (e) {
1555
- const matching = this.activeRequests.get(key);
1556
- if (matching != null) {
1557
- for (const p of matching.pending)
1558
- p.reject(e);
1559
- this.activeRequests.delete(key);
1560
- }
1561
- }
1562
- return await requestPromise;
1563
- }
1564
- /** Fires the appropriate onXXXRequested event based on the request type. */
1565
- async firePermissionRequestEvent(request, key) {
1566
- switch (request.type) {
1567
- case 'protocol':
1568
- await this.callEvent('onProtocolPermissionRequested', {
1569
- ...request,
1570
- counterparty: request.protocolID?.[0] === 1 ? undefined : request.counterparty,
1571
- requestID: key
1572
- });
1573
- break;
1574
- case 'basket':
1575
- await this.callEvent('onBasketAccessRequested', { ...request, requestID: key });
1576
- break;
1577
- case 'certificate':
1578
- await this.callEvent('onCertificateAccessRequested', { ...request, requestID: key });
1579
- break;
1580
- case 'spending':
1581
- await this.callEvent('onSpendingAuthorizationRequested', { ...request, requestID: key });
1582
- break;
1583
- }
1584
- }
1585
- /* ---------------------------------------------------------------------
1586
- * 4) SEARCH / DECODE / DECRYPT ON-CHAIN TOKENS (PushDrop Scripts)
1587
- * --------------------------------------------------------------------- */
1588
- /**
1589
- * We will use a administrative "permission token encryption" protocol to store fields
1590
- * in each permission's PushDrop script. This ensures that only the user's wallet
1591
- * can decrypt them. In practice, this data is not super sensitive, but we still
1592
- * follow the principle of least exposure.
1593
- */
1594
- static PERM_TOKEN_ENCRYPTION_PROTOCOL = [
1595
- 2,
1596
- 'admin permission token encryption'
1597
- ];
1598
- /**
1599
- * Similarly, we will use a "metadata encryption" protocol to preserve the confidentiality
1600
- * of transaction descriptions and input/output descriptions from lower storage layers.
1601
- */
1602
- static METADATA_ENCRYPTION_PROTOCOL = [
1603
- 2,
1604
- 'admin metadata encryption'
1605
- ];
1606
- /** We always use `keyID="1"` and `counterparty="self"` for these encryption ops. */
1607
- async encryptPermissionTokenField(plaintext) {
1608
- const data = typeof plaintext === 'string' ? sdk_1.Utils.toArray(plaintext, 'utf8') : plaintext;
1609
- const { ciphertext } = await this.underlying.encrypt({
1610
- plaintext: data,
1611
- protocolID: WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL,
1612
- keyID: '1'
1613
- }, this.adminOriginator);
1614
- return ciphertext;
1615
- }
1616
- /**
1617
- * Extracts a transaction from a `listOutputs` result's BEEF bundle, parsing
1618
- * the bundle only once per result. Calling
1619
- * `Transaction.fromBEEF(result.BEEF, txid)` per output re-parses the entire
1620
- * bundle every time — O(n²) in the number of outputs — which can block the
1621
- * caller's thread (in browser wallets, the UI) for seconds on large
1622
- * permission baskets.
1623
- */
1624
- transactionFromResultBeef(result, txid) {
1625
- const beef = result.BEEF;
1626
- if (beef != null) {
1627
- let parsed = this.parsedBeefCache.get(beef);
1628
- if (parsed === undefined) {
1629
- try {
1630
- parsed = sdk_1.Beef.fromBinary(beef);
1631
- }
1632
- catch {
1633
- parsed = null;
1634
- }
1635
- this.parsedBeefCache.set(beef, parsed);
1636
- }
1637
- // findAtomicTransaction (not findTxid().tx) matches fromBEEF exactly:
1638
- // it attaches merkle paths and sourceTransaction ancestry, which token
1639
- // consumers rely on when re-serializing via tx.toBEEF().
1640
- const tx = parsed?.findAtomicTransaction(txid);
1641
- if (tx != null)
1642
- return tx;
1643
- }
1644
- // Preserve the original behavior (including throwing) for bundles the
1645
- // fast path can't serve.
1646
- return sdk_1.Transaction.fromBEEF(beef ?? [], txid);
1647
- }
1648
- async decryptPermissionTokenField(ciphertext) {
1649
- // Field decrypts run in tight loops over every token in a basket; yield a
1650
- // macrotask periodically so large scans don't starve the caller's event
1651
- // loop (in browser wallets, that means blocking rendering and input).
1652
- if (++this.tokenFieldDecryptCount % 8 === 0) {
1653
- await new Promise(resolve => setTimeout(resolve, 0));
1654
- }
1655
- try {
1656
- const { plaintext } = await this.underlying.decrypt({
1657
- ciphertext,
1658
- protocolID: WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL,
1659
- keyID: '1'
1660
- }, this.adminOriginator);
1661
- return plaintext;
1662
- }
1663
- catch (_decryptionError) {
1664
- // Decryption failed (e.g. wrong key, unencrypted legacy value) — return the raw bytes unchanged
1665
- return ciphertext;
1666
- }
1667
- }
1668
- /**
1669
- * Encrypts wallet metadata if configured to do so, otherwise returns the original plaintext for storage.
1670
- * @param plaintext The metadata to encrypt if configured to do so
1671
- * @returns The encrypted metadata, or the original value if encryption was disabled.
1672
- */
1673
- async maybeEncryptMetadata(plaintext) {
1674
- if (!this.config.encryptWalletMetadata) {
1675
- return plaintext;
1676
- }
1677
- const { ciphertext } = await this.underlying.encrypt({
1678
- plaintext: sdk_1.Utils.toArray(plaintext, 'utf8'),
1679
- protocolID: WalletPermissionsManager.METADATA_ENCRYPTION_PROTOCOL,
1680
- keyID: '1'
1681
- }, this.adminOriginator);
1682
- return sdk_1.Utils.toBase64(ciphertext);
1683
- }
1684
- /**
1685
- * Attempts to decrypt metadata. if decryption fails, assumes the value is already plaintext and returns it.
1686
- * @param ciphertext The metadata to attempt decryption for.
1687
- * @returns The decrypted metadata. If decryption fails, returns the original value instead.
1688
- */
1689
- async maybeDecryptMetadata(ciphertext) {
1690
- try {
1691
- const { plaintext } = await this.underlying.decrypt({
1692
- ciphertext: sdk_1.Utils.toArray(ciphertext, 'base64'),
1693
- protocolID: WalletPermissionsManager.METADATA_ENCRYPTION_PROTOCOL,
1694
- keyID: '1'
1695
- }, this.adminOriginator);
1696
- return sdk_1.Utils.toUTF8(plaintext);
1697
- }
1698
- catch (_decryptionError) {
1699
- // Decryption failed (e.g. wrong key, unencrypted legacy value) — return original string unchanged
1700
- return ciphertext;
1701
- }
1702
- }
1703
- /** Helper to see if a token's expiry is in the past. */
1704
- isTokenExpired(expiry) {
1705
- const now = Math.floor(Date.now() / 1000);
1706
- return expiry > 0 && expiry < now;
1707
- }
1708
- /** Decrypts the standard 6 fields from a protocol (DPACP) PushDrop script. */
1709
- async decryptProtocolTokenFields(fields) {
1710
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[0]));
1711
- const expiryDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[1])), 10);
1712
- const privDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[2])) === 'true';
1713
- const secLevelDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[3])), 10);
1714
- const protoNameDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[4]));
1715
- const cptyDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fields[5]));
1716
- return { domainDecoded, expiryDecoded, privDecoded, secLevelDecoded, protoNameDecoded, cptyDecoded };
1717
- }
1718
- /** Parses outpoint string "txid.vout" into [txid, outputIndex]. */
1719
- parseOutpoint(outpoint) {
1720
- const [txid, indexStr] = outpoint.split('.');
1721
- return [txid, Number.parseInt(indexStr, 10)];
1722
- }
1723
- /** Normalizes a txid string to lowercase. */
1724
- normalizeTxid(txid) {
1725
- return (txid ?? '').toLowerCase();
1726
- }
1727
- /** Reverses a 32-byte hex txid (for endian normalization). */
1728
- reverseHexTxid(txid) {
1729
- const hex = this.normalizeTxid(txid);
1730
- if (!/^[0-9a-f]{64}$/.test(hex))
1731
- return hex;
1732
- const bytes = hex.match(/../g);
1733
- return (bytes != null) ? bytes.reverse().join('') : hex;
1734
- }
1735
- /**
1736
- * Returns true when an outpoint string (e.g. "txid.vout" or "txid:vout") refers to `token`.
1737
- */
1738
- tokenMatchesOutpointString(outpoint, token) {
1739
- const dot = outpoint.lastIndexOf('.');
1740
- const colon = outpoint.lastIndexOf(':');
1741
- const sep = Math.max(dot, colon);
1742
- if (sep === -1)
1743
- return false;
1744
- const txidPart = outpoint.slice(0, sep);
1745
- const vout = Number(outpoint.slice(sep + 1));
1746
- if (!Number.isFinite(vout))
1747
- return false;
1748
- return this.normalizeTxid(txidPart) === this.normalizeTxid(token.txid) && vout === token.outputIndex;
1749
- }
1750
- /**
1751
- * Finds the index of the tx input that spends the given permission token.
1752
- * Handles multiple potential field names for TXID and vout.
1753
- */
1754
- findInputIndexForToken(tx, token) {
1755
- return tx.inputs.findIndex((input) => {
1756
- const txidCandidate = input?.sourceTXID ??
1757
- input?.sourceTxid ??
1758
- input?.sourceTxId ??
1759
- input?.prevTxId ??
1760
- input?.prevTxid ??
1761
- input?.prevTXID ??
1762
- input?.txid ??
1763
- input?.txID;
1764
- const voutCandidate = input?.sourceOutputIndex ?? input?.sourceOutput ?? input?.outputIndex ?? input?.vout ?? input?.prevOutIndex;
1765
- if (typeof txidCandidate === 'string' && typeof voutCandidate === 'number') {
1766
- const cand = this.normalizeTxid(txidCandidate);
1767
- if (cand === this.normalizeTxid(token.txid) && voutCandidate === token.outputIndex)
1768
- return true;
1769
- if (cand === this.reverseHexTxid(token.txid) && voutCandidate === token.outputIndex)
1770
- return true;
1771
- }
1772
- const outpointCandidate = input?.outpoint ?? input?.sourceOutpoint ?? input?.prevOutpoint;
1773
- return typeof outpointCandidate === 'string' && this.tokenMatchesOutpointString(outpointCandidate, token);
1774
- });
1775
- }
1776
- /** Looks for a DPACP permission token matching origin/domain, privileged, protocol, cpty. */
1777
- async findProtocolToken(originator, privileged, protocolID, counterparty, includeExpired, originatorLookupValues) {
1778
- const [secLevel, protoName] = protocolID;
1779
- const originsToTry = originatorLookupValues?.length ? originatorLookupValues : [originator];
1780
- for (const originTag of originsToTry) {
1781
- const tags = [
1782
- `originator ${originTag}`,
1783
- `privileged ${!!privileged}`,
1784
- `protocolName ${protoName}`,
1785
- `protocolSecurityLevel ${secLevel}`
1786
- ];
1787
- if (secLevel === 2) {
1788
- tags.push(`counterparty ${counterparty}`);
1789
- }
1790
- const result = await this.underlying.listOutputs({
1791
- basket: BASKET_MAP.protocol,
1792
- tags,
1793
- tagQueryMode: 'all',
1794
- include: 'entire transactions'
1795
- }, this.adminOriginator);
1796
- for (const out of result.outputs) {
1797
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
1798
- const tx = this.transactionFromResultBeef(result, txid);
1799
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
1800
- if (dec?.fields == null || dec.fields.length < 6)
1801
- continue;
1802
- const f = await this.decryptProtocolTokenFields(dec.fields);
1803
- if (this.normalizeOriginator(f.domainDecoded) !== originator)
1804
- continue;
1805
- if (f.privDecoded !== !!privileged ||
1806
- f.secLevelDecoded !== secLevel ||
1807
- f.protoNameDecoded !== protoName ||
1808
- (f.secLevelDecoded === 2 && f.cptyDecoded !== counterparty)) {
1809
- continue;
1810
- }
1811
- if (!includeExpired && this.isTokenExpired(f.expiryDecoded))
1812
- continue;
1813
- return {
1814
- tx: tx.toBEEF(),
1815
- txid,
1816
- outputIndex,
1817
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
1818
- satoshis: out.satoshis,
1819
- originator,
1820
- rawOriginator: f.domainDecoded,
1821
- privileged,
1822
- protocol: protoName,
1823
- securityLevel: secLevel,
1824
- expiry: f.expiryDecoded,
1825
- counterparty: f.cptyDecoded
1826
- };
1827
- }
1828
- }
1829
- return undefined;
1830
- }
1831
- /** Finds ALL DPACP permission tokens matching origin/domain, privileged, protocol, cpty. Never filters by expiry. */
1832
- async findAllProtocolTokens(originator, privileged, protocolID, counterparty, originatorLookupValues) {
1833
- const [secLevel, protoName] = protocolID;
1834
- const originsToTry = originatorLookupValues?.length ? originatorLookupValues : [originator];
1835
- const matches = [];
1836
- const seen = new Set();
1837
- for (const originTag of originsToTry) {
1838
- const tags = [
1839
- `originator ${originTag}`,
1840
- `privileged ${!!privileged}`,
1841
- `protocolName ${protoName}`,
1842
- `protocolSecurityLevel ${secLevel}`
1843
- ];
1844
- if (secLevel === 2) {
1845
- tags.push(`counterparty ${counterparty}`);
1846
- }
1847
- const result = await this.underlying.listOutputs({
1848
- basket: BASKET_MAP.protocol,
1849
- tags,
1850
- tagQueryMode: 'all',
1851
- include: 'entire transactions'
1852
- }, this.adminOriginator);
1853
- for (const out of result.outputs) {
1854
- if (seen.has(out.outpoint))
1855
- continue;
1856
- const [txid, vout] = this.parseOutpoint(out.outpoint);
1857
- const tx = this.transactionFromResultBeef(result, txid);
1858
- const dec = sdk_1.PushDrop.decode(tx.outputs[vout].lockingScript);
1859
- if (dec?.fields == null || dec.fields.length < 6)
1860
- continue;
1861
- const f = await this.decryptProtocolTokenFields(dec.fields);
1862
- if (this.normalizeOriginator(f.domainDecoded) !== originator)
1863
- continue;
1864
- if (f.privDecoded !== !!privileged ||
1865
- f.secLevelDecoded !== secLevel ||
1866
- f.protoNameDecoded !== protoName ||
1867
- (f.secLevelDecoded === 2 && f.cptyDecoded !== counterparty)) {
1868
- continue;
1869
- }
1870
- seen.add(out.outpoint);
1871
- matches.push({
1872
- tx: tx.toBEEF(),
1873
- txid,
1874
- outputIndex: vout,
1875
- outputScript: tx.outputs[vout].lockingScript.toHex(),
1876
- satoshis: out.satoshis,
1877
- originator,
1878
- rawOriginator: f.domainDecoded,
1879
- privileged,
1880
- protocol: protoName,
1881
- securityLevel: secLevel,
1882
- expiry: f.expiryDecoded,
1883
- counterparty: f.cptyDecoded
1884
- });
1885
- }
1886
- }
1887
- return matches;
1888
- }
1889
- /** Looks for a DBAP token matching (originator, basket). */
1890
- async findBasketToken(originator, basket, includeExpired, originatorLookupValues) {
1891
- const originsToTry = originatorLookupValues?.length ? originatorLookupValues : [originator];
1892
- for (const originTag of originsToTry) {
1893
- const result = await this.underlying.listOutputs({
1894
- basket: BASKET_MAP.basket,
1895
- tags: [`originator ${originTag}`, `basket ${basket}`],
1896
- tagQueryMode: 'all',
1897
- include: 'entire transactions'
1898
- }, this.adminOriginator);
1899
- for (const out of result.outputs) {
1900
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
1901
- const tx = this.transactionFromResultBeef(result, txid);
1902
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
1903
- if (!dec?.fields || dec.fields.length < 3)
1904
- continue;
1905
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(dec.fields[0]));
1906
- if (this.normalizeOriginator(domainDecoded) !== originator)
1907
- continue;
1908
- const expiryDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(dec.fields[1])), 10);
1909
- const basketDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(dec.fields[2]));
1910
- if (basketDecoded !== basket)
1911
- continue;
1912
- if (!includeExpired && this.isTokenExpired(expiryDecoded))
1913
- continue;
1914
- return {
1915
- tx: tx.toBEEF(),
1916
- txid,
1917
- outputIndex,
1918
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
1919
- satoshis: out.satoshis,
1920
- originator,
1921
- rawOriginator: domainDecoded,
1922
- basketName: basketDecoded,
1923
- expiry: expiryDecoded
1924
- };
1925
- }
1926
- }
1927
- return undefined;
1928
- }
1929
- /** Looks for a DCAP token matching (origin, privileged, verifier, certType, fields subset). */
1930
- async findCertificateToken(originator, privileged, verifier, certType, fields, includeExpired, originatorLookupValues) {
1931
- const originsToTry = originatorLookupValues?.length ? originatorLookupValues : [originator];
1932
- for (const originTag of originsToTry) {
1933
- const result = await this.underlying.listOutputs({
1934
- basket: BASKET_MAP.certificate,
1935
- tags: [`originator ${originTag}`, `privileged ${!!privileged}`, `type ${certType}`, `verifier ${verifier}`],
1936
- tagQueryMode: 'all',
1937
- include: 'entire transactions'
1938
- }, this.adminOriginator);
1939
- for (const out of result.outputs) {
1940
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
1941
- const tx = this.transactionFromResultBeef(result, txid);
1942
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
1943
- if (!dec?.fields || dec.fields.length < 6)
1944
- continue;
1945
- const [domainRaw, expiryRaw, privRaw, typeRaw, fieldsRaw, verifierRaw] = dec.fields;
1946
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(domainRaw));
1947
- if (this.normalizeOriginator(domainDecoded) !== originator)
1948
- continue;
1949
- const expiryDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(expiryRaw)), 10);
1950
- const privDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(privRaw)) === 'true';
1951
- const typeDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(typeRaw));
1952
- const verifierDec = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(verifierRaw));
1953
- const allFields = JSON.parse(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fieldsRaw)));
1954
- if (privDecoded !== !!privileged || typeDecoded !== certType || verifierDec !== verifier)
1955
- continue;
1956
- // Check if 'fields' is a subset of 'allFields'
1957
- const setAll = new Set(allFields);
1958
- if (fields.some(f => !setAll.has(f)))
1959
- continue;
1960
- if (!includeExpired && this.isTokenExpired(expiryDecoded))
1961
- continue;
1962
- return {
1963
- tx: tx.toBEEF(),
1964
- txid,
1965
- outputIndex,
1966
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
1967
- satoshis: out.satoshis,
1968
- originator,
1969
- rawOriginator: domainDecoded,
1970
- privileged,
1971
- verifier: verifierDec,
1972
- certType: typeDecoded,
1973
- certFields: allFields,
1974
- expiry: expiryDecoded
1975
- };
1976
- }
1977
- }
1978
- return undefined;
1979
- }
1980
- /** Looks for a DSAP token matching origin, returning the first one found. */
1981
- async findSpendingToken(originator, originatorLookupValues) {
1982
- const originsToTry = originatorLookupValues?.length ? originatorLookupValues : [originator];
1983
- for (const originTag of originsToTry) {
1984
- const result = await this.underlying.listOutputs({
1985
- basket: BASKET_MAP.spending,
1986
- tags: [`originator ${originTag}`],
1987
- tagQueryMode: 'all',
1988
- include: 'entire transactions'
1989
- }, this.adminOriginator);
1990
- for (const out of result.outputs) {
1991
- const [txid, outputIndexStr] = out.outpoint.split('.');
1992
- const tx = this.transactionFromResultBeef(result, txid);
1993
- const dec = sdk_1.PushDrop.decode(tx.outputs[Number(outputIndexStr)].lockingScript);
1994
- if (!dec?.fields || dec.fields.length < 2)
1995
- continue;
1996
- const domainRaw = dec.fields[0];
1997
- const amtRaw = dec.fields[1];
1998
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(domainRaw));
1999
- const normalizedDomain = this.normalizeOriginator(domainDecoded);
2000
- if (normalizedDomain !== originator)
2001
- continue;
2002
- const amtDecodedStr = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(amtRaw));
2003
- const authorizedAmount = Number.parseInt(amtDecodedStr, 10);
2004
- return {
2005
- tx: tx.toBEEF(),
2006
- txid: out.outpoint.split('.')[0],
2007
- outputIndex: Number.parseInt(out.outpoint.split('.')[1], 10),
2008
- outputScript: tx.outputs[Number(outputIndexStr)].lockingScript.toHex(),
2009
- satoshis: out.satoshis,
2010
- originator,
2011
- rawOriginator: domainDecoded,
2012
- authorizedAmount,
2013
- expiry: 0 // Not time-limited, monthly authorization
2014
- };
2015
- }
2016
- }
2017
- return undefined;
2018
- }
2019
- /**
2020
- * Returns the current month and year in UTC as a string in the format "YYYY-MM".
2021
- *
2022
- * @returns {string} The current month and year in UTC.
2023
- */
2024
- getCurrentMonthYearUTC() {
2025
- const now = new Date();
2026
- const year = now.getUTCFullYear();
2027
- const month = (now.getUTCMonth() + 1).toString().padStart(2, '0'); // Ensure 2-digit month
2028
- return `${year}-${month}`;
2029
- }
2030
- /**
2031
- * Returns spending for an originator in the current calendar month.
2032
- */
2033
- async querySpentSince(token) {
2034
- const labelOrigins = this.buildOriginatorLookupValues(token.rawOriginator, token.originator);
2035
- let total = 0;
2036
- for (const labelOrigin of labelOrigins) {
2037
- const { actions } = await this.underlying.listActions({
2038
- labels: [`admin originator ${labelOrigin}`, `admin month ${this.getCurrentMonthYearUTC()}`],
2039
- labelQueryMode: 'all'
2040
- }, this.adminOriginator);
2041
- total += actions.reduce((a, e) => a - e.satoshis, 0);
2042
- }
2043
- return total;
2044
- }
2045
- /* ---------------------------------------------------------------------
2046
- * 5) CREATE / RENEW / REVOKE PERMISSION TOKENS ON CHAIN
2047
- * --------------------------------------------------------------------- */
2048
- /**
2049
- * Creates a brand-new permission token as a single-output PushDrop script in the relevant admin basket.
2050
- *
2051
- * The main difference between each type of token is in the "fields" we store in the PushDrop script.
2052
- *
2053
- * @param r The permission request
2054
- * @param expiry The expiry epoch time
2055
- * @param amount For DSAP, the authorized spending limit
2056
- */
2057
- async createPermissionOnChain(r, expiry, amount) {
2058
- const normalizedOriginator = this.normalizeOriginator(r.originator) || r.originator;
2059
- r.originator = normalizedOriginator;
2060
- const basketName = BASKET_MAP[r.type];
2061
- if (!basketName)
2062
- return;
2063
- // Build the array of encrypted fields for the PushDrop script
2064
- const fields = await this.buildPushdropFields(r, expiry, amount);
2065
- // Construct the script. We do a simple P2PK check. We ask `PushDrop.lock(...)`
2066
- // to create a script with a single OP_CHECKSIG verifying ownership to redeem.
2067
- const script = await new sdk_1.PushDrop(this.underlying).lock(fields, WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', true, true);
2068
- // Create tags
2069
- const tags = this.buildTagsForRequest(r);
2070
- // Build a transaction with exactly one output, no explicit inputs since the wallet
2071
- // can internally fund it from its balance.
2072
- await this.createAction({
2073
- description: `Grant ${r.type} permission`,
2074
- outputs: [
2075
- {
2076
- lockingScript: script.toHex(),
2077
- satoshis: 1,
2078
- outputDescription: `${r.type} permission token`,
2079
- basket: basketName,
2080
- tags
2081
- }
2082
- ],
2083
- options: {
2084
- acceptDelayedBroadcast: true
2085
- }
2086
- }, this.adminOriginator);
2087
- }
2088
- async mapWithConcurrency(items, concurrency, fn) {
2089
- if (items.length === 0)
2090
- return [];
2091
- const results = new Array(items.length);
2092
- let i = 0;
2093
- const worker = async () => {
2094
- while (true) {
2095
- const idx = i++;
2096
- if (idx >= items.length)
2097
- return;
2098
- results[idx] = await fn(items[idx]);
2099
- }
2100
- };
2101
- await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, async () => await worker()));
2102
- return results;
2103
- }
2104
- async runBestEffortBatches(items, chunkSize, runChunk) {
2105
- if (items.length === 0)
2106
- return [];
2107
- const out = [];
2108
- for (let i = 0; i < items.length; i += chunkSize) {
2109
- const chunk = items.slice(i, i + chunkSize);
2110
- out.push(...(await this.runBestEffortChunk(chunk, runChunk)));
2111
- }
2112
- return out;
2113
- }
2114
- async runBestEffortChunk(chunk, runChunk) {
2115
- try {
2116
- return await runChunk(chunk);
2117
- }
2118
- catch (e) {
2119
- if (chunk.length <= 1) {
2120
- console.error('Permission batch failed:', e);
2121
- return [];
2122
- }
2123
- const mid = Math.ceil(chunk.length / 2);
2124
- const left = await this.runBestEffortChunk(chunk.slice(0, mid), runChunk);
2125
- const right = await this.runBestEffortChunk(chunk.slice(mid), runChunk);
2126
- return [...left, ...right];
2127
- }
2128
- }
2129
- async buildPermissionOutput(r, expiry, amount) {
2130
- const normalizedOriginator = this.normalizeOriginator(r.originator) || r.originator;
2131
- r.originator = normalizedOriginator;
2132
- const basketName = BASKET_MAP[r.type];
2133
- if (!basketName) {
2134
- throw new Error(`Unsupported permission type: ${r.type}`);
2135
- }
2136
- const fields = await this.buildPushdropFields(r, expiry, amount);
2137
- const script = await new sdk_1.PushDrop(this.underlying).lock(fields, WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', true, true);
2138
- const tags = this.buildTagsForRequest(r);
2139
- return {
2140
- request: r,
2141
- output: {
2142
- lockingScript: script.toHex(),
2143
- satoshis: 1,
2144
- outputDescription: `${r.type} permission token`,
2145
- basket: basketName,
2146
- tags
2147
- }
2148
- };
2149
- }
2150
- async createPermissionTokensBestEffort(items) {
2151
- const CHUNK = 25;
2152
- return await this.runBestEffortBatches(items, CHUNK, async (chunk) => {
2153
- const built = await this.mapWithConcurrency(chunk, 8, async (c) => await this.buildPermissionOutput(c.request, c.expiry, c.amount));
2154
- await this.createAction({
2155
- description: `Grant ${built.length} permissions`,
2156
- outputs: built.map(b => b.output),
2157
- options: { acceptDelayedBroadcast: true }
2158
- }, this.adminOriginator);
2159
- return built.map(b => b.request);
2160
- });
2161
- }
2162
- async renewPermissionTokensBestEffort(items) {
2163
- const CHUNK = 15;
2164
- return await this.runBestEffortBatches(items, CHUNK, async (chunk) => {
2165
- const built = await this.mapWithConcurrency(chunk, 8, async (c) => await this.buildPermissionOutput(c.request, c.expiry, c.amount));
2166
- const inputBeef = new sdk_1.Beef();
2167
- for (const c of chunk) {
2168
- inputBeef.mergeBeef(sdk_1.Beef.fromBinary(c.oldToken.tx));
2169
- }
2170
- const { signableTransaction } = await this.createAction({
2171
- description: `Renew ${chunk.length} permissions`,
2172
- inputBEEF: inputBeef.toBinary(),
2173
- inputs: chunk.map((c, i) => ({
2174
- outpoint: `${c.oldToken.txid}.${c.oldToken.outputIndex}`,
2175
- unlockingScriptLength: 73,
2176
- inputDescription: `Consume old permission token #${i + 1}`
2177
- })),
2178
- outputs: built.map(b => b.output),
2179
- options: {
2180
- acceptDelayedBroadcast: true,
2181
- randomizeOutputs: false,
2182
- signAndProcess: false
2183
- }
2184
- }, this.adminOriginator);
2185
- if (!signableTransaction?.reference || !signableTransaction.tx) {
2186
- throw new Error('Failed to create signable transaction');
2187
- }
2188
- const partialTx = sdk_1.Transaction.fromAtomicBEEF(signableTransaction.tx);
2189
- const pushdrop = new sdk_1.PushDrop(this.underlying);
2190
- const spends = {};
2191
- for (let i = 0; i < chunk.length; i++) {
2192
- const token = chunk[i].oldToken;
2193
- const unlocker = pushdrop.unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(token.outputScript));
2194
- const unlockingScript = await unlocker.sign(partialTx, i);
2195
- spends[i] = { unlockingScript: unlockingScript.toHex() };
2196
- }
2197
- const { txid } = await this.underlying.signAction({
2198
- reference: signableTransaction.reference,
2199
- spends
2200
- });
2201
- if (!txid)
2202
- throw new Error('Failed to finalize renewal transaction');
2203
- return built.map(b => b.request);
2204
- });
2205
- }
2206
- async coalescePermissionTokens(oldTokens, newScript, opts) {
2207
- if (!oldTokens?.length)
2208
- throw new Error('No permission tokens to coalesce');
2209
- if (oldTokens.length < 2)
2210
- throw new Error('Need at least 2 tokens to coalesce');
2211
- // 1) Create a signable action with N inputs and a single renewed output
2212
- // Merge all input token BEEFs into a single BEEF structure
2213
- const inputBeef = new sdk_1.Beef();
2214
- for (const token of oldTokens) {
2215
- inputBeef.mergeBeef(sdk_1.Beef.fromBinary(token.tx));
2216
- }
2217
- const { signableTransaction } = await this.createAction({
2218
- description: opts?.description ?? `Coalesce ${oldTokens.length} permission tokens`,
2219
- inputBEEF: inputBeef.toBinary(),
2220
- inputs: oldTokens.map((t, i) => ({
2221
- outpoint: `${t.txid}.${t.outputIndex}`,
2222
- unlockingScriptLength: 74,
2223
- inputDescription: `Consume permission token #${i + 1}`
2224
- })),
2225
- outputs: [
2226
- {
2227
- lockingScript: newScript.toHex(),
2228
- satoshis: 1,
2229
- outputDescription: 'Renewed permission token',
2230
- ...(opts?.basket ? { basket: opts.basket } : {}),
2231
- ...((opts?.tags == null) ? {} : { tags: opts.tags })
2232
- }
2233
- ],
2234
- options: {
2235
- acceptDelayedBroadcast: true,
2236
- randomizeOutputs: false,
2237
- signAndProcess: false
2238
- }
2239
- }, this.adminOriginator);
2240
- if (!signableTransaction?.reference || !signableTransaction.tx) {
2241
- throw new Error('Failed to create signable transaction');
2242
- }
2243
- // 2) Sign each input - each token needs its own unlocker with the correct locking script
2244
- const partialTx = sdk_1.Transaction.fromAtomicBEEF(signableTransaction.tx);
2245
- const pushdrop = new sdk_1.PushDrop(this.underlying);
2246
- const spends = {};
2247
- for (let i = 0; i < oldTokens.length; i++) {
2248
- const token = oldTokens[i];
2249
- // Each token requires its own unlocker with the specific locking script
2250
- const unlocker = pushdrop.unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(token.outputScript));
2251
- const unlockingScript = await unlocker.sign(partialTx, i);
2252
- spends[i] = { unlockingScript: unlockingScript.toHex() };
2253
- }
2254
- // 3) Finalize the action
2255
- const { txid } = await this.underlying.signAction({
2256
- reference: signableTransaction.reference,
2257
- spends
2258
- });
2259
- if (!txid)
2260
- throw new Error('Failed to finalize coalescing transaction');
2261
- return txid;
2262
- }
2263
- /**
2264
- * Renews a permission token by spending the old token as input and creating a new token output.
2265
- * This invalidates the old token and replaces it with a new one.
2266
- *
2267
- * @param oldToken The old token to consume
2268
- * @param r The permission request being renewed
2269
- * @param newExpiry The new expiry epoch time
2270
- * @param newAmount For DSAP, the new authorized amount
2271
- */
2272
- async renewPermissionOnChain(oldToken, r, newExpiry, newAmount) {
2273
- r.originator = this.normalizeOriginator(r.originator) || r.originator;
2274
- // 1) build new fields
2275
- const newFields = await this.buildPushdropFields(r, newExpiry, newAmount);
2276
- // 2) new script
2277
- const newScript = await new sdk_1.PushDrop(this.underlying).lock(newFields, WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', true, true);
2278
- const tags = this.buildTagsForRequest(r);
2279
- // Check if there are multiple old tokens for the same parameters (shouldn't usually happen)
2280
- const oldTokens = await this.findAllProtocolTokens(oldToken.originator, oldToken.privileged, [oldToken.securityLevel, oldToken.protocol], oldToken.counterparty, this.buildOriginatorLookupValues(oldToken.rawOriginator, oldToken.originator));
2281
- // If so, coalesce them into a single token first, to avoid bloat
2282
- if (oldTokens.length > 1) {
2283
- await this.coalescePermissionTokens(oldTokens, newScript, {
2284
- tags,
2285
- basket: BASKET_MAP[r.type],
2286
- description: `Coalesce ${r.type} permission tokens`
2287
- });
2288
- }
2289
- else {
2290
- // Otherwise, just proceed with the single-token renewal
2291
- // 3) For BRC-100, we do a "createAction" with a partial input referencing oldToken
2292
- // plus a single new output. We'll hydrate the template, then signAction for the wallet to finalize.
2293
- const oldOutpoint = `${oldToken.txid}.${oldToken.outputIndex}`;
2294
- const { signableTransaction } = await this.createAction({
2295
- description: `Renew ${r.type} permission`,
2296
- inputBEEF: oldToken.tx,
2297
- inputs: [
2298
- {
2299
- outpoint: oldOutpoint,
2300
- unlockingScriptLength: 73, // length of signature
2301
- inputDescription: `Consume old ${r.type} token`
2302
- }
2303
- ],
2304
- outputs: [
2305
- {
2306
- lockingScript: newScript.toHex(),
2307
- satoshis: 1,
2308
- outputDescription: `Renewed ${r.type} permission token`,
2309
- basket: BASKET_MAP[r.type],
2310
- tags
2311
- }
2312
- ],
2313
- options: {
2314
- acceptDelayedBroadcast: true
2315
- }
2316
- }, this.adminOriginator);
2317
- const tx = sdk_1.Transaction.fromBEEF(signableTransaction.tx);
2318
- const unlocker = new sdk_1.PushDrop(this.underlying).unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(oldToken.outputScript));
2319
- const unlockingScript = await unlocker.sign(tx, 0);
2320
- await this.underlying.signAction({
2321
- reference: signableTransaction.reference,
2322
- spends: {
2323
- 0: {
2324
- unlockingScript: unlockingScript.toHex()
2325
- }
2326
- }
2327
- });
2328
- }
2329
- }
2330
- /**
2331
- * Builds the encrypted array of fields for a PushDrop permission token
2332
- * (protocol / basket / certificate / spending).
2333
- */
2334
- async buildPushdropFields(r, expiry, amount) {
2335
- switch (r.type) {
2336
- case 'protocol': {
2337
- const [secLevel, protoName] = r.protocolID;
2338
- const counterparty = secLevel === 2 ? (r.counterparty ?? 'self') : '';
2339
- return [
2340
- await this.encryptPermissionTokenField(r.originator), // domain
2341
- await this.encryptPermissionTokenField(String(expiry)), // expiry
2342
- await this.encryptPermissionTokenField(r.privileged === true ? 'true' : 'false'),
2343
- await this.encryptPermissionTokenField(String(secLevel)),
2344
- await this.encryptPermissionTokenField(protoName),
2345
- await this.encryptPermissionTokenField(counterparty)
2346
- ];
2347
- }
2348
- case 'basket': {
2349
- return [
2350
- await this.encryptPermissionTokenField(r.originator),
2351
- await this.encryptPermissionTokenField(String(expiry)),
2352
- await this.encryptPermissionTokenField(r.basket)
2353
- ];
2354
- }
2355
- case 'certificate': {
2356
- const { certType, fields, verifier } = r.certificate;
2357
- return [
2358
- await this.encryptPermissionTokenField(r.originator),
2359
- await this.encryptPermissionTokenField(String(expiry)),
2360
- await this.encryptPermissionTokenField(r.privileged ? 'true' : 'false'),
2361
- await this.encryptPermissionTokenField(certType),
2362
- await this.encryptPermissionTokenField(JSON.stringify(fields)),
2363
- await this.encryptPermissionTokenField(verifier)
2364
- ];
2365
- }
2366
- case 'spending': {
2367
- // DSAP
2368
- const authAmt = amount ?? (r.spending?.satoshis || 0);
2369
- return [
2370
- await this.encryptPermissionTokenField(r.originator),
2371
- await this.encryptPermissionTokenField(String(authAmt))
2372
- ];
2373
- }
2374
- }
2375
- }
2376
- /**
2377
- * Helper to build an array of tags for the new output, matching the user request's
2378
- * origin, basket, privileged, protocol name, etc.
2379
- */
2380
- buildTagsForRequest(r) {
2381
- const tags = [`originator ${r.originator}`];
2382
- switch (r.type) {
2383
- case 'protocol': {
2384
- tags.push(`privileged ${!!r.privileged}`, `protocolName ${r.protocolID[1]}`, `protocolSecurityLevel ${r.protocolID[0]}`);
2385
- if (r.protocolID[0] === 2) {
2386
- tags.push(`counterparty ${r.counterparty ?? 'self'}`);
2387
- }
2388
- break;
2389
- }
2390
- case 'basket': {
2391
- tags.push(`basket ${r.basket}`);
2392
- break;
2393
- }
2394
- case 'certificate': {
2395
- tags.push(`privileged ${!!r.privileged}`, `type ${r.certificate.certType}`, `verifier ${r.certificate.verifier}`);
2396
- break;
2397
- }
2398
- case 'spending': {
2399
- // Only 'originator' is strictly required as a tag.
2400
- break;
2401
- }
2402
- }
2403
- return tags;
2404
- }
2405
- /* ---------------------------------------------------------------------
2406
- * 6) PUBLIC "LIST/HAS/REVOKE" METHODS
2407
- * --------------------------------------------------------------------- */
2408
- /**
2409
- * Lists all protocol permission tokens (DPACP) with optional filters.
2410
- * @param originator Optional originator domain to filter by
2411
- * @param privileged Optional boolean to filter by privileged status
2412
- * @param protocolName Optional protocol name to filter by
2413
- * @param protocolSecurityLevel Optional protocol security level to filter by
2414
- * @param counterparty Optional counterparty to filter by
2415
- * @returns Array of permission tokens that match the filter criteria
2416
- */
2417
- async listProtocolPermissions({ originator, privileged, protocolName, protocolSecurityLevel, counterparty } = {}) {
2418
- const baseTags = this.buildProtocolFilterTags({ privileged, protocolName, protocolSecurityLevel, counterparty });
2419
- const originFilter = originator ? this.prepareOriginator(originator) : undefined;
2420
- const originVariants = (originFilter == null) ? [undefined] : originFilter.lookupValues;
2421
- const seen = new Set();
2422
- const tokens = [];
2423
- for (const originTag of originVariants) {
2424
- const tags = originTag != null ? [...baseTags, `originator ${originTag}`] : [...baseTags];
2425
- const result = await this.underlying.listOutputs({ basket: BASKET_MAP.protocol, tags, tagQueryMode: 'all', include: 'entire transactions', limit: 10000 }, this.adminOriginator);
2426
- await this.collectProtocolTokens(result, originFilter, seen, tokens);
2427
- }
2428
- return tokens;
2429
- }
2430
- /** Builds the base tag array for protocol permission listing. */
2431
- buildProtocolFilterTags({ privileged, protocolName, protocolSecurityLevel, counterparty }) {
2432
- const tags = [];
2433
- if (privileged !== undefined)
2434
- tags.push(`privileged ${!!privileged}`);
2435
- if (protocolName)
2436
- tags.push(`protocolName ${protocolName}`);
2437
- if (protocolSecurityLevel !== undefined)
2438
- tags.push(`protocolSecurityLevel ${protocolSecurityLevel}`);
2439
- if (counterparty)
2440
- tags.push(`counterparty ${counterparty}`);
2441
- return tags;
2442
- }
2443
- /** Decodes and appends protocol permission tokens from a listOutputs result. */
2444
- async collectProtocolTokens(result, originFilter, seen, tokens) {
2445
- for (const out of result.outputs) {
2446
- if (seen.has(out.outpoint))
2447
- continue;
2448
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
2449
- const tx = this.transactionFromResultBeef(result, txid);
2450
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
2451
- if (!dec?.fields || dec.fields.length < 6)
2452
- continue;
2453
- const f = await this.decryptProtocolTokenFields(dec.fields);
2454
- const normalizedDomain = this.normalizeOriginator(f.domainDecoded);
2455
- if ((originFilter != null) && normalizedDomain !== originFilter.normalized)
2456
- continue;
2457
- seen.add(out.outpoint);
2458
- tokens.push({
2459
- tx: tx.toBEEF(), txid, outputIndex,
2460
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
2461
- satoshis: out.satoshis, originator: normalizedDomain, rawOriginator: f.domainDecoded,
2462
- expiry: f.expiryDecoded, privileged: f.privDecoded,
2463
- securityLevel: f.secLevelDecoded, protocol: f.protoNameDecoded, counterparty: f.cptyDecoded
2464
- });
2465
- }
2466
- }
2467
- /**
2468
- * Returns true if the originator already holds a valid unexpired protocol permission.
2469
- * This calls `ensureProtocolPermission` with `seekPermission=false`, so it won't prompt.
2470
- */
2471
- async hasProtocolPermission(params) {
2472
- try {
2473
- await this.ensureProtocolPermission({
2474
- ...params,
2475
- reason: 'hasProtocolPermission',
2476
- seekPermission: false,
2477
- usageType: 'generic'
2478
- });
2479
- return true;
2480
- }
2481
- catch {
2482
- return false;
2483
- }
2484
- }
2485
- /**
2486
- * Lists basket permission tokens (DBAP) for a given originator or basket (or for all if not specified).
2487
- * @param params.originator Optional originator to filter by
2488
- * @param params.basket Optional basket name to filter by
2489
- * @returns Array of permission tokens that match the filter criteria
2490
- */
2491
- async listBasketAccess(params = {}) {
2492
- const basketName = BASKET_MAP.basket;
2493
- const baseTags = [];
2494
- if (params.basket) {
2495
- baseTags.push(`basket ${params.basket}`);
2496
- }
2497
- const originFilter = params.originator ? this.prepareOriginator(params.originator) : undefined;
2498
- const originVariants = (originFilter == null) ? [undefined] : originFilter.lookupValues;
2499
- const seen = new Set();
2500
- const tokens = [];
2501
- for (const originTag of originVariants) {
2502
- const tags = [...baseTags];
2503
- if (originTag) {
2504
- tags.push(`originator ${originTag}`);
2505
- }
2506
- const result = await this.underlying.listOutputs({
2507
- basket: basketName,
2508
- tags,
2509
- tagQueryMode: 'all',
2510
- include: 'entire transactions',
2511
- limit: 10000
2512
- }, this.adminOriginator);
2513
- for (const out of result.outputs) {
2514
- if (seen.has(out.outpoint))
2515
- continue;
2516
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
2517
- const tx = this.transactionFromResultBeef(result, txid);
2518
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
2519
- if (!dec?.fields || dec.fields.length < 3)
2520
- continue;
2521
- const [domainRaw, expiryRaw, basketRaw] = dec.fields;
2522
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(domainRaw));
2523
- const normalizedDomain = this.normalizeOriginator(domainDecoded);
2524
- if ((originFilter != null) && normalizedDomain !== originFilter.normalized)
2525
- continue;
2526
- const expiryDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(expiryRaw)), 10);
2527
- const basketDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(basketRaw));
2528
- seen.add(out.outpoint);
2529
- tokens.push({
2530
- tx: tx.toBEEF(),
2531
- txid,
2532
- outputIndex,
2533
- satoshis: out.satoshis,
2534
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
2535
- originator: normalizedDomain,
2536
- rawOriginator: domainDecoded,
2537
- basketName: basketDecoded,
2538
- expiry: expiryDecoded
2539
- });
2540
- }
2541
- }
2542
- return tokens;
2543
- }
2544
- /**
2545
- * Returns `true` if the originator already holds a valid unexpired basket permission for `basket`.
2546
- */
2547
- async hasBasketAccess(params) {
2548
- try {
2549
- await this.ensureBasketAccess({
2550
- originator: params.originator,
2551
- basket: params.basket,
2552
- seekPermission: false,
2553
- usageType: 'insertion'
2554
- });
2555
- return true;
2556
- }
2557
- catch {
2558
- return false;
2559
- }
2560
- }
2561
- /**
2562
- * Lists spending authorization tokens (DSAP) for a given originator (or all).
2563
- */
2564
- async listSpendingAuthorizations(params) {
2565
- const basketName = BASKET_MAP.spending;
2566
- const tags = [];
2567
- if (params.originator) {
2568
- tags.push(`originator ${params.originator}`);
2569
- }
2570
- const result = await this.underlying.listOutputs({
2571
- basket: basketName,
2572
- tags,
2573
- tagQueryMode: 'all',
2574
- include: 'entire transactions',
2575
- limit: 10000
2576
- }, this.adminOriginator);
2577
- const tokens = [];
2578
- for (const out of result.outputs) {
2579
- const [txid, outputIndexStr] = out.outpoint.split('.');
2580
- const tx = this.transactionFromResultBeef(result, txid);
2581
- const dec = sdk_1.PushDrop.decode(tx.outputs[Number(outputIndexStr)].lockingScript);
2582
- if (!dec?.fields || dec.fields.length < 2)
2583
- continue;
2584
- const [domainRaw, amtRaw] = dec.fields;
2585
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(domainRaw));
2586
- const amtDecodedStr = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(amtRaw));
2587
- const authorizedAmount = Number.parseInt(amtDecodedStr, 10);
2588
- tokens.push({
2589
- tx: tx.toBEEF(),
2590
- txid: out.outpoint.split('.')[0],
2591
- outputIndex: Number.parseInt(out.outpoint.split('.')[1], 10),
2592
- satoshis: out.satoshis,
2593
- outputScript: tx.outputs[Number(outputIndexStr)].lockingScript.toHex(),
2594
- originator: domainDecoded,
2595
- authorizedAmount,
2596
- expiry: 0
2597
- });
2598
- }
2599
- return tokens;
2600
- }
2601
- /**
2602
- * Returns `true` if the originator already holds a valid spending authorization token
2603
- * with enough available monthly spend. We do not prompt (seekPermission=false).
2604
- */
2605
- async hasSpendingAuthorization(params) {
2606
- try {
2607
- await this.ensureSpendingAuthorization({
2608
- originator: params.originator,
2609
- satoshis: params.satoshis,
2610
- seekPermission: false
2611
- });
2612
- return true;
2613
- }
2614
- catch {
2615
- return false;
2616
- }
2617
- }
2618
- /**
2619
- * Lists certificate permission tokens (DCAP) with optional filters.
2620
- * @param originator Optional originator domain to filter by
2621
- * @param privileged Optional boolean to filter by privileged status
2622
- * @param certType Optional certificate type to filter by
2623
- * @param verifier Optional verifier to filter by
2624
- * @returns Array of permission tokens that match the filter criteria
2625
- */
2626
- async listCertificateAccess(params = {}) {
2627
- const baseTags = [];
2628
- if (params.privileged !== undefined)
2629
- baseTags.push(`privileged ${!!params.privileged}`);
2630
- if (params.certType)
2631
- baseTags.push(`type ${params.certType}`);
2632
- if (params.verifier)
2633
- baseTags.push(`verifier ${params.verifier}`);
2634
- const originFilter = params.originator ? this.prepareOriginator(params.originator) : undefined;
2635
- const originVariants = (originFilter == null) ? [undefined] : originFilter.lookupValues;
2636
- const seen = new Set();
2637
- const tokens = [];
2638
- for (const originTag of originVariants) {
2639
- const tags = originTag != null ? [...baseTags, `originator ${originTag}`] : [...baseTags];
2640
- const result = await this.underlying.listOutputs({ basket: BASKET_MAP.certificate, tags, tagQueryMode: 'all', include: 'entire transactions', limit: 10000 }, this.adminOriginator);
2641
- await this.collectCertificateTokens(result, originFilter, seen, tokens);
2642
- }
2643
- return tokens;
2644
- }
2645
- /** Decodes and appends certificate permission tokens from a listOutputs result. */
2646
- async collectCertificateTokens(result, originFilter, seen, tokens) {
2647
- for (const out of result.outputs) {
2648
- if (seen.has(out.outpoint))
2649
- continue;
2650
- const [txid, outputIndex] = this.parseOutpoint(out.outpoint);
2651
- const tx = this.transactionFromResultBeef(result, txid);
2652
- const dec = sdk_1.PushDrop.decode(tx.outputs[outputIndex].lockingScript);
2653
- if (!dec?.fields || dec.fields.length < 6)
2654
- continue;
2655
- const [domainRaw, expiryRaw, privRaw, typeRaw, fieldsRaw, verifierRaw] = dec.fields;
2656
- const domainDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(domainRaw));
2657
- const normalizedDomain = this.normalizeOriginator(domainDecoded);
2658
- if ((originFilter != null) && normalizedDomain !== originFilter.normalized)
2659
- continue;
2660
- const expiryDecoded = Number.parseInt(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(expiryRaw)), 10);
2661
- const privDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(privRaw)) === 'true';
2662
- const typeDecoded = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(typeRaw));
2663
- const verifierDec = sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(verifierRaw));
2664
- const allFields = JSON.parse(sdk_1.Utils.toUTF8(await this.decryptPermissionTokenField(fieldsRaw)));
2665
- seen.add(out.outpoint);
2666
- tokens.push({
2667
- tx: tx.toBEEF(), txid, outputIndex, satoshis: out.satoshis,
2668
- outputScript: tx.outputs[outputIndex].lockingScript.toHex(),
2669
- originator: normalizedDomain, rawOriginator: domainDecoded,
2670
- privileged: privDecoded, certType: typeDecoded, certFields: allFields,
2671
- verifier: verifierDec, expiry: expiryDecoded
2672
- });
2673
- }
2674
- }
2675
- /**
2676
- * Returns `true` if the originator already holds a valid unexpired certificate access
2677
- * for the given certType/fields. Does not prompt the user.
2678
- */
2679
- async hasCertificateAccess(params) {
2680
- try {
2681
- await this.ensureCertificateAccess({
2682
- originator: params.originator,
2683
- privileged: params.privileged,
2684
- verifier: params.verifier,
2685
- certType: params.certType,
2686
- fields: params.fields,
2687
- seekPermission: false,
2688
- usageType: 'disclosure'
2689
- });
2690
- return true;
2691
- }
2692
- catch {
2693
- return false;
2694
- }
2695
- }
2696
- async revokePermissions(oldTokens) {
2697
- return await this.revokePermissionTokensBestEffort(oldTokens);
2698
- }
2699
- async revokeAllForOriginator(originator, opts) {
2700
- const preparedOriginator = this.prepareOriginator(originator);
2701
- const include = {
2702
- protocol: true,
2703
- basket: true,
2704
- certificate: true,
2705
- spending: true,
2706
- ...opts
2707
- };
2708
- const [protocolTokens, basketTokens, certificateTokens, spendingTokens] = await Promise.all([
2709
- include.protocol ? this.listProtocolPermissions({ originator }) : Promise.resolve([]),
2710
- include.basket ? this.listBasketAccess({ originator }) : Promise.resolve([]),
2711
- include.certificate ? this.listCertificateAccess({ originator }) : Promise.resolve([]),
2712
- include.spending
2713
- ? this.listSpendingAuthorizations({ originator: preparedOriginator.normalized })
2714
- : Promise.resolve([])
2715
- ]);
2716
- const spendingTokenList = (spendingTokens.length > 0) ? [spendingTokens[0]] : [];
2717
- const allTokens = [...protocolTokens, ...basketTokens, ...certificateTokens, ...spendingTokenList];
2718
- const seen = new Set();
2719
- const deduped = allTokens.filter(t => {
2720
- const key = `${t.txid}.${t.outputIndex}`;
2721
- if (seen.has(key))
2722
- return false;
2723
- seen.add(key);
2724
- return true;
2725
- });
2726
- return await this.revokePermissions(deduped);
2727
- }
2728
- async revokePermissionTokensBestEffort(items) {
2729
- const CHUNK = 15;
2730
- return await this.runBestEffortBatches(items, CHUNK, async (chunk) => {
2731
- await this.revokePermissionTokensChunk(chunk);
2732
- return chunk;
2733
- });
2734
- }
2735
- async revokePermissionTokensChunk(oldTokens) {
2736
- if (oldTokens.length === 0)
2737
- return;
2738
- const inputBeef = new sdk_1.Beef();
2739
- for (const token of oldTokens) {
2740
- inputBeef.mergeBeef(sdk_1.Beef.fromBinary(token.tx));
2741
- }
2742
- const { signableTransaction } = await this.createAction({
2743
- description: `Revoke ${oldTokens.length} permissions`,
2744
- inputBEEF: inputBeef.toBinary(),
2745
- inputs: oldTokens.map((t, i) => ({
2746
- outpoint: `${t.txid}.${t.outputIndex}`,
2747
- unlockingScriptLength: 73,
2748
- inputDescription: `Consume old permission token #${i + 1}`
2749
- })),
2750
- options: {
2751
- acceptDelayedBroadcast: true,
2752
- randomizeOutputs: false,
2753
- signAndProcess: false
2754
- }
2755
- }, this.adminOriginator);
2756
- if (!signableTransaction?.reference || !signableTransaction.tx) {
2757
- throw new Error('Failed to create signable transaction');
2758
- }
2759
- const tx = sdk_1.Transaction.fromAtomicBEEF(signableTransaction.tx);
2760
- const inputsToSign = oldTokens.map(token => {
2761
- let permInputIndex = this.findInputIndexForToken(tx, token);
2762
- if (permInputIndex === -1 && tx.inputs.length === 1) {
2763
- permInputIndex = 0;
2764
- }
2765
- if (permInputIndex === -1) {
2766
- throw new Error('Unable to locate permission token input for revocation.');
2767
- }
2768
- return { token, permInputIndex };
2769
- });
2770
- const pushdrop = new sdk_1.PushDrop(this.underlying);
2771
- const spends = {};
2772
- const signed = await this.mapWithConcurrency(inputsToSign, 8, async ({ token, permInputIndex }) => {
2773
- const unlocker = pushdrop.unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(token.outputScript));
2774
- const unlockingScript = await unlocker.sign(tx, permInputIndex);
2775
- return { permInputIndex, unlockingScriptHex: unlockingScript.toHex() };
2776
- });
2777
- for (const s of signed) {
2778
- spends[s.permInputIndex] = { unlockingScript: s.unlockingScriptHex };
2779
- }
2780
- const { txid } = await this.underlying.signAction({
2781
- reference: signableTransaction.reference,
2782
- spends
2783
- });
2784
- if (!txid)
2785
- throw new Error('Failed to finalize revoke transaction');
2786
- }
2787
- /**
2788
- * Revokes a permission token by spending it with no replacement output.
2789
- * The manager builds a BRC-100 transaction that consumes the token, effectively invalidating it.
2790
- */
2791
- async revokePermission(oldToken) {
2792
- const oldOutpoint = `${oldToken.txid}.${oldToken.outputIndex}`;
2793
- const { signableTransaction } = await this.createAction({
2794
- description: 'Revoke permission',
2795
- inputBEEF: oldToken.tx,
2796
- inputs: [
2797
- {
2798
- outpoint: oldOutpoint,
2799
- unlockingScriptLength: 73, // length of signature
2800
- inputDescription: 'Consume old permission token'
2801
- }
2802
- ],
2803
- options: {
2804
- acceptDelayedBroadcast: true
2805
- }
2806
- }, this.adminOriginator);
2807
- const tx = sdk_1.Transaction.fromBEEF(signableTransaction.tx);
2808
- let permInputIndex = this.findInputIndexForToken(tx, oldToken);
2809
- if (permInputIndex === -1 && tx.inputs.length === 1) {
2810
- permInputIndex = 0;
2811
- }
2812
- if (permInputIndex === -1) {
2813
- throw new Error('Unable to locate permission token input for revocation.');
2814
- }
2815
- const unlocker = new sdk_1.PushDrop(this.underlying).unlock(WalletPermissionsManager.PERM_TOKEN_ENCRYPTION_PROTOCOL, '1', 'self', 'all', false, 1, sdk_1.LockingScript.fromHex(oldToken.outputScript));
2816
- const unlockingScript = await unlocker.sign(tx, permInputIndex);
2817
- await this.underlying.signAction({
2818
- reference: signableTransaction.reference,
2819
- spends: {
2820
- [permInputIndex]: {
2821
- unlockingScript: unlockingScript.toHex()
2822
- }
2823
- }
2824
- });
2825
- }
2826
- /* ---------------------------------------------------------------------
2827
- * 7) BRC-100 WALLET INTERFACE FORWARDING WITH PERMISSION CHECKS
2828
- * --------------------------------------------------------------------- */
2829
- async createAction(args, originator) {
2830
- // 1) Identify unique P-modules involved (one per schemeID) from both baskets and labels
2831
- const pModulesByScheme = new Map();
2832
- const nonPBaskets = this.collectNonPBaskets(args.outputs, pModulesByScheme);
2833
- const nonPLabels = this.splitLabelsByPermissionModule(args.labels, pModulesByScheme);
2834
- // 2) Check permissions for non-P baskets
2835
- for (const basket of nonPBaskets) {
2836
- await this.ensureBasketAccess({ originator: originator, basket, reason: args.description, usageType: 'insertion' });
2837
- }
2838
- // 3) Check permissions for non-P labels
2839
- for (const lbl of nonPLabels) {
2840
- await this.ensureLabelAccess({ originator: originator, label: lbl, reason: args.description, usageType: 'apply' });
2841
- }
2842
- // 4) Force signAndProcess=false unless the originator is admin and explicitly sets it to true.
2843
- const modifiedOptions = this.enforceSignAndProcess(args.options, originator);
2844
- // 5) Encrypt transaction metadata, saving originals for spending auth and line items.
2845
- const originalDescription = args.description;
2846
- const { originalInputDescriptions, originalOutputDescriptions } = await this.encryptActionMetadata(args);
2847
- // 6) Call the underlying wallet's createAction with P-module chaining as needed.
2848
- const finalArgs = {
2849
- ...args,
2850
- options: modifiedOptions,
2851
- labels: [...(args.labels || []), `admin originator ${originator}`, `admin month ${this.getCurrentMonthYearUTC()}`]
2852
- };
2853
- let createResult = await this.callCreateActionWithPModules(finalArgs, pModulesByScheme, originator);
2854
- if (createResult.signableTransaction == null)
2855
- return createResult;
2856
- // 7) Parse the signable tx to determine net spend, then gate on spending authorization.
2857
- const tx = sdk_1.Transaction.fromAtomicBEEF(createResult.signableTransaction.tx);
2858
- const reference = createResult.signableTransaction.reference;
2859
- // 7a) SECURITY (GHSA-36f9-7rg5-cpf8) defense-in-depth: confirm every
2860
- // caller-requested output actually appears in the transaction we are about to
2861
- // authorize and sign. The locking scripts in the signable transaction
2862
- // originate from storage; a malicious or compromised remote storage provider
2863
- // could substitute a recipient script, which would then be signed and
2864
- // broadcast while this manager's caller/UI still shows the requested
2865
- // recipient. The signer (buildSignableTransaction) rejects this at the
2866
- // source; this is an independent check at the permissions layer.
2867
- try {
2868
- this.verifyRequestedOutputsPresent(tx, args);
2869
- }
2870
- catch (err) {
2871
- await this.underlying.abortAction({ reference });
2872
- throw err;
2873
- }
2874
- const { netSpent, lineItems } = this.computeNetSpend(tx, args, originalInputDescriptions, originalOutputDescriptions);
2875
- // 8) If netSpent > 0, require spending authorization. Abort if denied.
2876
- if (netSpent > 0) {
2877
- try {
2878
- await this.ensureSpendingAuthorization({ originator: originator, satoshis: netSpent, lineItems, reason: originalDescription });
2879
- }
2880
- catch (err) {
2881
- await this.underlying.abortAction({ reference });
2882
- throw err;
2883
- }
2884
- }
2885
- // 9) Finalize or return the signable transaction based on whether more signatures are needed.
2886
- const vargs = sdk_1.Validation.validateCreateActionArgs(args);
2887
- if (vargs.isSignAction)
2888
- return createResult;
2889
- const signResult = await this.underlying.signAction({ reference, spends: {}, options: args.options }, originator);
2890
- return { ...createResult, ...signResult, signableTransaction: undefined };
2891
- }
2892
- /** Scans outputs to split P-scheme baskets from regular baskets; registers P-modules as a side effect. */
2893
- collectNonPBaskets(outputs, pModulesByScheme) {
2894
- const nonPBaskets = [];
2895
- if (outputs == null)
2896
- return nonPBaskets;
2897
- for (const out of outputs) {
2898
- if (!out.basket)
2899
- continue;
2900
- if (out.basket.startsWith('p ')) {
2901
- this.addPModuleByScheme(out.basket.split(' ')[1], 'basket', pModulesByScheme);
2902
- }
2903
- else {
2904
- nonPBaskets.push(out.basket);
2905
- }
2906
- }
2907
- return nonPBaskets;
2908
- }
2909
- /** Enforces signAndProcess=false for non-admin originators; throws if admin override is missing. */
2910
- enforceSignAndProcess(options, originator) {
2911
- const modifiedOptions = { ...options };
2912
- if (modifiedOptions.signAndProcess !== true) {
2913
- modifiedOptions.signAndProcess = false;
2914
- }
2915
- else if (!this.isAdminOriginator(originator)) {
2916
- throw new Error('Only the admin originator can set signAndProcess=true explicitly.');
2917
- }
2918
- return modifiedOptions;
2919
- }
2920
- /** Encrypts all description/instruction fields in args in-place; returns original (plaintext) copies. */
2921
- async encryptActionMetadata(args) {
2922
- const originalInputDescriptions = {};
2923
- const originalOutputDescriptions = {};
2924
- const inputEncryptionTasks = (args.inputs || []).map(async (input, i) => {
2925
- if (!input.inputDescription)
2926
- return;
2927
- originalInputDescriptions[i] = input.inputDescription;
2928
- input.inputDescription = await this.maybeEncryptMetadata(input.inputDescription);
2929
- });
2930
- const outputEncryptionTasks = (args.outputs || []).map(async (output, i) => {
2931
- if (output.outputDescription) {
2932
- originalOutputDescriptions[i] = output.outputDescription;
2933
- output.outputDescription = await this.maybeEncryptMetadata(output.outputDescription);
2934
- }
2935
- if (output.customInstructions) {
2936
- output.customInstructions = await this.maybeEncryptMetadata(output.customInstructions);
2937
- }
2938
- });
2939
- await Promise.all([
2940
- (async () => { args.description = await this.maybeEncryptMetadata(args.description); })(),
2941
- ...inputEncryptionTasks,
2942
- ...outputEncryptionTasks
2943
- ]);
2944
- return { originalInputDescriptions, originalOutputDescriptions };
2945
- }
2946
- /** Calls underlying createAction, chaining P-module request/response transforms when needed. */
2947
- async callCreateActionWithPModules(finalArgs, pModulesByScheme, originator) {
2948
- if (pModulesByScheme.size === 0)
2949
- return await this.underlying.createAction(finalArgs, originator);
2950
- const pModules = Array.from(pModulesByScheme.values());
2951
- let transformedArgs = finalArgs;
2952
- for (const module of pModules) {
2953
- const transformed = await module.onRequest({ method: 'createAction', args: transformedArgs, originator: originator });
2954
- transformedArgs = transformed.args;
2955
- }
2956
- let createResult = await this.underlying.createAction(transformedArgs, originator);
2957
- for (let i = pModules.length - 1; i >= 0; i--) {
2958
- createResult = await pModules[i].onResponse(createResult, { method: 'createAction', originator: originator });
2959
- }
2960
- return createResult;
2961
- }
2962
- /**
2963
- * Computes the net satoshis the originator is spending in a createAction call,
2964
- * accounting for foreign (originator-provided) inputs and outputs plus the fee.
2965
- * Also builds the line items list for the spending authorization request.
2966
- */
2967
- /**
2968
- * Defense-in-depth verification that each caller-requested output (locking
2969
- * script + amount) is present in the transaction returned for signing.
2970
- *
2971
- * The scripts in the signable transaction originate from storage. A malicious
2972
- * or compromised remote storage provider could substitute a different
2973
- * recipient script for a caller-specified output. The signer
2974
- * (buildSignableTransaction) rejects this at the source; this is an
2975
- * independent check at the permissions layer so the substitution is caught
2976
- * regardless of how the signable transaction was produced.
2977
- *
2978
- * Outputs are matched as a multiset on (lockingScript, satoshis): the
2979
- * transaction also contains change/commission outputs and the output order is
2980
- * randomized by default, and a caller may legitimately request the same
2981
- * script+amount more than once.
2982
- *
2983
- * @throws Error if any caller-requested output is absent from the transaction.
2984
- */
2985
- verifyRequestedOutputsPresent(tx, args) {
2986
- const requested = args.outputs || [];
2987
- if (requested.length === 0)
2988
- return;
2989
- // All transaction outputs as (script hex, satoshis); each may satisfy at
2990
- // most one requested output.
2991
- const available = tx.outputs.map(o => ({
2992
- script: o.lockingScript.toHex().toLowerCase(),
2993
- satoshis: o.satoshis,
2994
- used: false
2995
- }));
2996
- for (let i = 0; i < requested.length; i++) {
2997
- const wantScript = (requested[i].lockingScript ?? '').toLowerCase();
2998
- const wantSats = requested[i].satoshis;
2999
- const match = available.find(a => !a.used && a.script === wantScript && a.satoshis === wantSats);
3000
- if (match == null) {
3001
- throw new Error(`The transaction returned for signing does not contain caller-requested output ${i} ` +
3002
- `(locking script and amount). The recipient may have been substituted by storage.`);
3003
- }
3004
- match.used = true;
3005
- }
3006
- }
3007
- computeNetSpend(tx, args, originalInputDescriptions, originalOutputDescriptions) {
3008
- const lineItems = [];
3009
- // Sum originator-provided inputs:
3010
- let totalInputSatoshis = 0;
3011
- for (const input of tx.inputs) {
3012
- const outpoint = `${input.sourceTXID}.${input.sourceOutputIndex}`;
3013
- const matchingIndex = (args.inputs || []).findIndex(i => i.outpoint === outpoint);
3014
- if (matchingIndex !== -1) {
3015
- const satoshis = input.sourceTransaction.outputs[input.sourceOutputIndex].satoshis;
3016
- totalInputSatoshis += satoshis;
3017
- lineItems.push({
3018
- type: 'input',
3019
- description: originalInputDescriptions[matchingIndex] || 'No input description provided',
3020
- satoshis
3021
- });
3022
- }
3023
- }
3024
- // Sum originator-requested outputs:
3025
- const totalOutputSatoshis = (args.outputs || []).reduce((acc, out) => acc + out.satoshis, 0);
3026
- for (const outIndex in args.outputs || []) {
3027
- const out = args.outputs[outIndex];
3028
- lineItems.push({
3029
- type: 'output',
3030
- satoshis: out.satoshis,
3031
- description: originalOutputDescriptions[outIndex] || 'No output description provided'
3032
- });
3033
- }
3034
- // Add an entry for the transaction fee:
3035
- const fee = tx.getFee();
3036
- lineItems.push({ type: 'fee', satoshis: fee, description: 'Network fee' });
3037
- /**
3038
- * Net spend = total foreign outflows minus total foreign inflows plus fee.
3039
- * Domestic wallet inputs/change outputs are not counted — they are internal.
3040
- */
3041
- const netSpent = totalOutputSatoshis + fee - totalInputSatoshis;
3042
- return { netSpent, lineItems };
3043
- }
3044
- async signAction(...args) {
3045
- return await this.underlying.signAction(...args);
3046
- }
3047
- async abortAction(...args) {
3048
- return await this.underlying.abortAction(...args);
3049
- }
3050
- async listActions(...args) {
3051
- const [requestArgs, originator] = args;
3052
- const { remainingLabels: labelsForPermissionChecks } = (0, brc114ActionTimeLabels_1.parseBrc114ActionTimeLabels)(requestArgs.labels);
3053
- // 1) Identify unique P-modules involved (one per schemeID, preserving label order)
3054
- const pModulesByScheme = new Map();
3055
- const nonPLabels = this.splitLabelsByPermissionModule(labelsForPermissionChecks, pModulesByScheme);
3056
- // 2) Check permissions for non-P labels
3057
- for (const lbl of nonPLabels) {
3058
- await this.ensureLabelAccess({
3059
- originator: originator,
3060
- label: lbl,
3061
- reason: 'listActions',
3062
- usageType: 'list'
3063
- });
3064
- }
3065
- // 3) Call underlying wallet, with P-module transformations if needed
3066
- let results;
3067
- if (pModulesByScheme.size > 0) {
3068
- // P-modules are involved - chain transformations
3069
- const pModules = Array.from(pModulesByScheme.values());
3070
- // Chain onRequest calls through all modules in order
3071
- let transformedArgs = requestArgs;
3072
- for (const module of pModules) {
3073
- const transformed = await module.onRequest({
3074
- method: 'listActions',
3075
- args: transformedArgs,
3076
- originator: originator
3077
- });
3078
- transformedArgs = transformed.args;
3079
- }
3080
- // Call underlying wallet with transformed args
3081
- results = await this.underlying.listActions(transformedArgs, originator);
3082
- // Chain onResponse calls in reverse order
3083
- for (let i = pModules.length - 1; i >= 0; i--) {
3084
- results = await pModules[i].onResponse(results, {
3085
- method: 'listActions',
3086
- originator: originator
3087
- });
3088
- }
3089
- }
3090
- else {
3091
- // No P-modules - call underlying wallet directly
3092
- results = await this.underlying.listActions(...args);
3093
- }
3094
- // 4) Transparently decrypt transaction metadata, if configured to do so.
3095
- return await this.decryptListActionsMetadata(results);
3096
- }
3097
- async internalizeAction(...args) {
3098
- const [requestArgs, originator] = args;
3099
- // 1) Identify unique P-modules involved (one per schemeID) from both baskets and labels
3100
- const pModulesByScheme = new Map();
3101
- const nonPBaskets = [];
3102
- // Check baskets for p modules
3103
- for (const outIndex in requestArgs.outputs) {
3104
- const out = requestArgs.outputs[outIndex];
3105
- if (out.protocol === 'basket insertion') {
3106
- const basket = out.insertionRemittance.basket;
3107
- if (basket.startsWith('p ')) {
3108
- const schemeID = basket.split(' ')[1];
3109
- this.addPModuleByScheme(schemeID, 'basket', pModulesByScheme);
3110
- }
3111
- else {
3112
- // Track non-P baskets for normal permission checks
3113
- nonPBaskets.push({
3114
- outIndex,
3115
- basket,
3116
- customInstructions: out.insertionRemittance.customInstructions
3117
- });
3118
- }
3119
- }
3120
- }
3121
- // Check labels for p modules
3122
- const nonPLabels = this.splitLabelsByPermissionModule(requestArgs.labels, pModulesByScheme);
3123
- // 2) Check permissions for non-P baskets
3124
- for (const { outIndex, basket, customInstructions } of nonPBaskets) {
3125
- await this.ensureBasketAccess({
3126
- originator: originator,
3127
- basket,
3128
- reason: requestArgs.description,
3129
- usageType: 'insertion'
3130
- });
3131
- if (customInstructions) {
3132
- requestArgs.outputs[outIndex].insertionRemittance.customInstructions =
3133
- await this.maybeEncryptMetadata(customInstructions);
3134
- }
3135
- }
3136
- // 3) Check permissions for non-P labels
3137
- for (const lbl of nonPLabels) {
3138
- await this.ensureLabelAccess({
3139
- originator: originator,
3140
- label: lbl,
3141
- reason: requestArgs.description,
3142
- usageType: 'apply'
3143
- });
3144
- }
3145
- // 4) Call underlying wallet, with P-module transformations if needed
3146
- if (pModulesByScheme.size > 0) {
3147
- // P-modules are involved - chain transformations
3148
- const pModules = Array.from(pModulesByScheme.values());
3149
- // Chain onRequest calls through all modules in order
3150
- let transformedArgs = requestArgs;
3151
- for (const module of pModules) {
3152
- const transformed = await module.onRequest({
3153
- method: 'internalizeAction',
3154
- args: transformedArgs,
3155
- originator: originator
3156
- });
3157
- transformedArgs = transformed.args;
3158
- }
3159
- // Encrypt custom instructions for p basket outputs
3160
- for (const outIndex in transformedArgs.outputs) {
3161
- const out = transformedArgs.outputs[outIndex];
3162
- if (out.protocol === 'basket insertion' && out.insertionRemittance?.customInstructions) {
3163
- out.insertionRemittance.customInstructions = await this.maybeEncryptMetadata(out.insertionRemittance.customInstructions);
3164
- }
3165
- }
3166
- // Call underlying wallet with transformed args
3167
- let results = await this.underlying.internalizeAction(transformedArgs, originator);
3168
- // Chain onResponse calls in reverse order
3169
- for (let i = pModules.length - 1; i >= 0; i--) {
3170
- results = await pModules[i].onResponse(results, {
3171
- method: 'internalizeAction',
3172
- originator: originator
3173
- });
3174
- }
3175
- return results;
3176
- }
3177
- // No P-modules - call underlying wallet directly
3178
- return await this.underlying.internalizeAction(...args);
3179
- }
3180
- async listOutputs(...args) {
3181
- const [requestArgs, originator] = args;
3182
- // Delegate to permission module if needed
3183
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.basket, 'listOutputs', requestArgs, originator, async (transformedArgs) => {
3184
- const result = await this.underlying.listOutputs(transformedArgs, originator);
3185
- // Apply metadata decryption to permission module response
3186
- return await this.decryptListOutputsMetadata(result);
3187
- });
3188
- if (pModuleResult !== null) {
3189
- return pModuleResult;
3190
- }
3191
- // Ensure the originator has permission for the basket.
3192
- await this.ensureBasketAccess({
3193
- originator: originator,
3194
- basket: requestArgs.basket,
3195
- reason: 'listOutputs',
3196
- usageType: 'listing'
3197
- });
3198
- const results = await this.underlying.listOutputs(...args);
3199
- // Apply metadata decryption to regular response
3200
- return await this.decryptListOutputsMetadata(results);
3201
- }
3202
- async relinquishOutput(...args) {
3203
- const [requestArgs, originator] = args;
3204
- // Delegate to permission module if needed
3205
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.basket, 'relinquishOutput', requestArgs, originator, async (transformedArgs) => {
3206
- return await this.underlying.relinquishOutput(transformedArgs, originator);
3207
- });
3208
- if (pModuleResult !== null) {
3209
- return pModuleResult;
3210
- }
3211
- await this.ensureBasketAccess({
3212
- originator: originator,
3213
- basket: requestArgs.basket,
3214
- reason: 'relinquishOutput',
3215
- usageType: 'removal'
3216
- });
3217
- return await this.underlying.relinquishOutput(...args);
3218
- }
3219
- async getPublicKey(...args) {
3220
- const [requestArgs, originator] = args;
3221
- if (requestArgs.protocolID != null) {
3222
- // Delegate to permission module if needed
3223
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'getPublicKey', requestArgs, originator, async (transformedArgs) => {
3224
- return await this.underlying.getPublicKey(transformedArgs, originator);
3225
- });
3226
- if (pModuleResult !== null) {
3227
- return pModuleResult;
3228
- }
3229
- // Not a P-protocol, continue with normal permission flow
3230
- await this.ensureProtocolPermission({
3231
- originator: originator,
3232
- privileged: requestArgs.privileged,
3233
- protocolID: requestArgs.protocolID,
3234
- counterparty: requestArgs.counterparty || 'self',
3235
- reason: requestArgs.privilegedReason,
3236
- seekPermission: requestArgs.seekPermission,
3237
- usageType: 'publicKey'
3238
- });
3239
- }
3240
- if (requestArgs.identityKey) {
3241
- // We also require a minimal protocol permission to retrieve the user's identity key
3242
- await this.ensureProtocolPermission({
3243
- originator: originator,
3244
- privileged: requestArgs.privileged,
3245
- protocolID: [1, 'identity key retrieval'],
3246
- counterparty: 'self',
3247
- reason: requestArgs.privilegedReason,
3248
- seekPermission: requestArgs.seekPermission,
3249
- usageType: 'identityKey'
3250
- });
3251
- }
3252
- return await this.underlying.getPublicKey(...args);
3253
- }
3254
- async revealCounterpartyKeyLinkage(...args) {
3255
- const [requestArgs, originator] = args;
3256
- await this.ensureProtocolPermission({
3257
- originator: originator,
3258
- privileged: requestArgs.privileged,
3259
- protocolID: [2, `counterparty key linkage revelation ${requestArgs.counterparty}`],
3260
- counterparty: requestArgs.verifier,
3261
- reason: requestArgs.privilegedReason,
3262
- usageType: 'linkageRevelation'
3263
- });
3264
- return await this.underlying.revealCounterpartyKeyLinkage(...args);
3265
- }
3266
- async revealSpecificKeyLinkage(...args) {
3267
- const [requestArgs, originator] = args;
3268
- await this.ensureProtocolPermission({
3269
- originator: originator,
3270
- privileged: requestArgs.privileged,
3271
- protocolID: [
3272
- 2,
3273
- `specific key linkage revelation ${requestArgs.protocolID[1]} ${requestArgs.protocolID[0] === 2 ? requestArgs.keyID : 'all'}`
3274
- ],
3275
- counterparty: requestArgs.verifier,
3276
- reason: requestArgs.privilegedReason,
3277
- usageType: 'linkageRevelation'
3278
- });
3279
- return await this.underlying.revealSpecificKeyLinkage(...args);
3280
- }
3281
- async encrypt(...args) {
3282
- const [requestArgs, originator] = args;
3283
- // Delegate to permission module if needed
3284
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'encrypt', requestArgs, originator, async (transformedArgs) => {
3285
- return await this.underlying.encrypt(transformedArgs, originator);
3286
- });
3287
- if (pModuleResult !== null) {
3288
- return pModuleResult;
3289
- }
3290
- await this.ensureProtocolPermission({
3291
- originator: originator,
3292
- protocolID: requestArgs.protocolID,
3293
- privileged: requestArgs.privileged,
3294
- counterparty: requestArgs.counterparty || 'self',
3295
- reason: requestArgs.privilegedReason,
3296
- usageType: 'encrypting'
3297
- });
3298
- return await this.underlying.encrypt(...args);
3299
- }
3300
- async decrypt(...args) {
3301
- const [requestArgs, originator] = args;
3302
- // Delegate to permission module if needed
3303
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'decrypt', requestArgs, originator, async (transformedArgs) => {
3304
- return await this.underlying.decrypt(transformedArgs, originator);
3305
- });
3306
- if (pModuleResult !== null) {
3307
- return pModuleResult;
3308
- }
3309
- await this.ensureProtocolPermission({
3310
- originator: originator,
3311
- privileged: requestArgs.privileged,
3312
- protocolID: requestArgs.protocolID,
3313
- counterparty: requestArgs.counterparty || 'self',
3314
- reason: requestArgs.privilegedReason,
3315
- usageType: 'encrypting'
3316
- });
3317
- return await this.underlying.decrypt(...args);
3318
- }
3319
- async createHmac(...args) {
3320
- const [requestArgs, originator] = args;
3321
- // Delegate to permission module if needed
3322
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'createHmac', requestArgs, originator, async (transformedArgs) => {
3323
- return await this.underlying.createHmac(transformedArgs, originator);
3324
- });
3325
- if (pModuleResult !== null) {
3326
- return pModuleResult;
3327
- }
3328
- await this.ensureProtocolPermission({
3329
- originator: originator,
3330
- privileged: requestArgs.privileged,
3331
- protocolID: requestArgs.protocolID,
3332
- counterparty: requestArgs.counterparty || 'self',
3333
- reason: requestArgs.privilegedReason,
3334
- usageType: 'hmac'
3335
- });
3336
- return await this.underlying.createHmac(...args);
3337
- }
3338
- async verifyHmac(...args) {
3339
- const [requestArgs, originator] = args;
3340
- // Delegate to permission module if needed
3341
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'verifyHmac', requestArgs, originator, async (transformedArgs) => {
3342
- return await this.underlying.verifyHmac(transformedArgs, originator);
3343
- });
3344
- if (pModuleResult !== null) {
3345
- return pModuleResult;
3346
- }
3347
- await this.ensureProtocolPermission({
3348
- originator: originator,
3349
- privileged: requestArgs.privileged,
3350
- protocolID: requestArgs.protocolID,
3351
- counterparty: requestArgs.counterparty || 'self',
3352
- reason: requestArgs.privilegedReason,
3353
- usageType: 'hmac'
3354
- });
3355
- return await this.underlying.verifyHmac(...args);
3356
- }
3357
- async createSignature(...args) {
3358
- const [requestArgs, originator] = args;
3359
- // Delegate to permission module if needed
3360
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'createSignature', requestArgs, originator, async (transformedArgs) => {
3361
- return await this.underlying.createSignature(transformedArgs, originator);
3362
- });
3363
- if (pModuleResult !== null) {
3364
- return pModuleResult;
3365
- }
3366
- await this.ensureProtocolPermission({
3367
- originator: originator,
3368
- privileged: requestArgs.privileged,
3369
- protocolID: requestArgs.protocolID,
3370
- counterparty: requestArgs.counterparty || 'self',
3371
- reason: requestArgs.privilegedReason,
3372
- usageType: 'signing'
3373
- });
3374
- return await this.underlying.createSignature(...args);
3375
- }
3376
- async verifySignature(...args) {
3377
- const [requestArgs, originator] = args;
3378
- // Delegate to permission module if needed
3379
- const pModuleResult = await this.delegateToPModuleIfNeeded(requestArgs.protocolID[1], 'verifySignature', requestArgs, originator, async (transformedArgs) => {
3380
- return await this.underlying.verifySignature(transformedArgs, originator);
3381
- });
3382
- if (pModuleResult !== null) {
3383
- return pModuleResult;
3384
- }
3385
- await this.ensureProtocolPermission({
3386
- originator: originator,
3387
- privileged: requestArgs.privileged,
3388
- protocolID: requestArgs.protocolID,
3389
- counterparty: requestArgs.counterparty || 'self',
3390
- reason: requestArgs.privilegedReason,
3391
- usageType: 'signing'
3392
- });
3393
- return await this.underlying.verifySignature(...args);
3394
- }
3395
- async acquireCertificate(...args) {
3396
- const [requestArgs, originator] = args;
3397
- if (this.config.seekCertificateAcquisitionPermissions) {
3398
- await this.ensureProtocolPermission({
3399
- originator: originator,
3400
- privileged: requestArgs.privileged,
3401
- protocolID: [1, `certificate acquisition ${requestArgs.type}`],
3402
- counterparty: 'self',
3403
- reason: requestArgs.privilegedReason,
3404
- usageType: 'generic'
3405
- });
3406
- }
3407
- return await this.underlying.acquireCertificate(...args);
3408
- }
3409
- async listCertificates(...args) {
3410
- const [requestArgs, originator] = args;
3411
- if (this.config.seekCertificateListingPermissions) {
3412
- await this.ensureProtocolPermission({
3413
- originator: originator,
3414
- privileged: requestArgs.privileged,
3415
- protocolID: [1, 'certificate list'],
3416
- counterparty: 'self',
3417
- reason: requestArgs.privilegedReason,
3418
- usageType: 'generic'
3419
- });
3420
- }
3421
- return await this.underlying.listCertificates(...args);
3422
- }
3423
- async proveCertificate(...args) {
3424
- const [requestArgs, originator] = args;
3425
- await this.ensureCertificateAccess({
3426
- originator: originator,
3427
- privileged: requestArgs.privileged,
3428
- verifier: requestArgs.verifier,
3429
- certType: requestArgs.certificate.type,
3430
- fields: requestArgs.fieldsToReveal,
3431
- reason: 'proveCertificate',
3432
- usageType: 'disclosure'
3433
- });
3434
- return await this.underlying.proveCertificate(...args);
3435
- }
3436
- async relinquishCertificate(...args) {
3437
- const [requestArgs, originator] = args;
3438
- if (this.config.seekCertificateRelinquishmentPermissions) {
3439
- await this.ensureProtocolPermission({
3440
- originator: originator,
3441
- privileged: !!requestArgs.privileged,
3442
- protocolID: [1, `certificate relinquishment ${requestArgs.type}`],
3443
- counterparty: 'self',
3444
- reason: requestArgs.privilegedReason || 'relinquishCertificate',
3445
- usageType: 'generic'
3446
- });
3447
- }
3448
- return await this.underlying.relinquishCertificate(...args);
3449
- }
3450
- async discoverByIdentityKey(...args) {
3451
- const [_, originator] = args;
3452
- if (this.config.seekPermissionsForIdentityResolution) {
3453
- await this.ensureProtocolPermission({
3454
- originator: originator,
3455
- privileged: false,
3456
- protocolID: [1, 'identity resolution'],
3457
- counterparty: 'self',
3458
- reason: 'discoverByIdentityKey',
3459
- usageType: 'generic'
3460
- });
3461
- }
3462
- return await this.underlying.discoverByIdentityKey(...args);
3463
- }
3464
- async discoverByAttributes(...args) {
3465
- const [_, originator] = args;
3466
- if (this.config.seekPermissionsForIdentityResolution) {
3467
- await this.ensureProtocolPermission({
3468
- originator: originator,
3469
- privileged: false,
3470
- protocolID: [1, 'identity resolution'],
3471
- counterparty: 'self',
3472
- reason: 'discoverByAttributes',
3473
- usageType: 'generic'
3474
- });
3475
- }
3476
- return await this.underlying.discoverByAttributes(...args);
3477
- }
3478
- async isAuthenticated(...args) {
3479
- return await this.underlying.isAuthenticated(...args);
3480
- }
3481
- async waitForAuthentication(...args) {
3482
- const [_, originator] = args;
3483
- if (this.config.seekGroupedPermission && originator) {
3484
- const { normalized: normalizedOriginator } = this.prepareOriginator(originator);
3485
- const normalized = normalizedOriginator;
3486
- await this.withGroupedPermissionFlowLock(normalized, async () => {
3487
- // 1. Fetch manifest.json from the originator
3488
- const groupPermissions = await this.fetchManifestGroupPermissions(normalized);
3489
- if (groupPermissions != null) {
3490
- // 2. Filter out already-granted permissions
3491
- const permissionsToRequest = await this.filterAlreadyGrantedPermissions(normalized, groupPermissions);
3492
- // 3. If any permissions are left to request, start the flow
3493
- if (this.hasAnyPermissionsToRequest(permissionsToRequest)) {
3494
- const key = `group:${normalized}`;
3495
- if (this.activeRequests.has(key)) {
3496
- // Another call is already waiting, piggyback on it
3497
- await new Promise((resolve, reject) => {
3498
- this.activeRequests.get(key).pending.push({ resolve, reject });
3499
- });
3500
- }
3501
- else {
3502
- // This is the first call, create a new request
3503
- const promise = new Promise((resolve, reject) => {
3504
- this.activeRequests.set(key, {
3505
- request: { originator: normalized, permissions: permissionsToRequest },
3506
- pending: [{ resolve, reject }]
3507
- });
3508
- });
3509
- // Fire the event outside the executor to avoid async executor (S6544)
3510
- await this.callEvent('onGroupedPermissionRequested', {
3511
- requestID: key,
3512
- originator: normalized,
3513
- permissions: permissionsToRequest
3514
- });
3515
- await promise;
3516
- }
3517
- }
3518
- }
3519
- });
3520
- }
3521
- // Finally, after handling grouped permissions, call the underlying method.
3522
- return await this.underlying.waitForAuthentication(...args);
3523
- }
3524
- async getHeight(...args) {
3525
- return await this.underlying.getHeight(...args);
3526
- }
3527
- async getHeaderForHeight(...args) {
3528
- return await this.underlying.getHeaderForHeight(...args);
3529
- }
3530
- async getNetwork(...args) {
3531
- return await this.underlying.getNetwork(...args);
3532
- }
3533
- async getVersion(...args) {
3534
- return await this.underlying.getVersion(...args);
3535
- }
3536
- /* ---------------------------------------------------------------------
3537
- * 8) INTERNAL HELPER UTILITIES
3538
- * --------------------------------------------------------------------- */
3539
- /** Returns true if the specified origin is the admin originator. */
3540
- isAdminOriginator(originator) {
3541
- return this.normalizeOriginator(originator) === this.adminOriginator;
3542
- }
3543
- /**
3544
- * Checks if the given protocol is admin-reserved per BRC-100 rules:
3545
- *
3546
- * - Must not start with `admin` (admin-reserved)
3547
- * - Must not start with `p ` (allows for future specially permissioned protocols)
3548
- *
3549
- * If it violates these rules and the caller is not admin, we consider it "admin-only."
3550
- */
3551
- isAdminProtocol(proto) {
3552
- const protocolName = proto[1];
3553
- if (protocolName.startsWith('admin')) {
3554
- return true;
3555
- }
3556
- return false;
3557
- }
3558
- /**
3559
- * Checks if the given label is admin-reserved per BRC-100 rules:
3560
- *
3561
- * - Must not start with `admin` (admin-reserved)
3562
- * - Must not start with `p ` (permissioned labels requiring a permission module)
3563
- *
3564
- * If it violates these rules and the caller is not admin, we consider it "admin-only."
3565
- */
3566
- isAdminLabel(label) {
3567
- if (label.startsWith('admin')) {
3568
- return true;
3569
- }
3570
- if (label.startsWith('p ')) {
3571
- return true;
3572
- }
3573
- return false;
3574
- }
3575
- /**
3576
- * Checks if the given basket is admin-reserved per BRC-100 rules:
3577
- *
3578
- * - Must not start with `admin`
3579
- * - Must not be `default` (some wallets use this for internal operations)
3580
- * - Must not start with `p ` (future specially permissioned baskets)
3581
- */
3582
- isAdminBasket(basket) {
3583
- if (basket === 'default')
3584
- return true;
3585
- if (basket.startsWith('admin'))
3586
- return true;
3587
- return false;
3588
- }
3589
- /**
3590
- * Whether the permission is satisfied without consulting on-chain tokens:
3591
- * cached, recently granted, or — when the user just granted it and its
3592
- * token is still minting — after the in-flight mint settles. Spares the
3593
- * user a duplicate prompt for a grant they already made moments ago.
3594
- */
3595
- async hasRecentOrPendingGrant(cacheKey) {
3596
- if (this.isPermissionCached(cacheKey) || this.isRecentlyGranted(cacheKey))
3597
- return true;
3598
- const inFlight = this.mintsInFlight.get(cacheKey);
3599
- if (inFlight == null)
3600
- return false;
3601
- await inFlight;
3602
- return this.isPermissionCached(cacheKey) || this.isRecentlyGranted(cacheKey);
3603
- }
3604
- /**
3605
- * Returns true if we have a cached record that the permission identified by
3606
- * `key` is valid and unexpired.
3607
- */
3608
- isPermissionCached(key) {
3609
- const entry = this.permissionCache.get(key);
3610
- if (entry == null)
3611
- return false;
3612
- if (Date.now() - entry.cachedAt > WalletPermissionsManager.CACHE_TTL_MS) {
3613
- this.permissionCache.delete(key);
3614
- return false;
3615
- }
3616
- if (this.isTokenExpired(entry.expiry)) {
3617
- this.permissionCache.delete(key);
3618
- return false;
3619
- }
3620
- return true;
3621
- }
3622
- /** Caches the fact that the permission for `key` is valid until `expiry`. */
3623
- cachePermission(key, expiry) {
3624
- this.permissionCache.set(key, { expiry, cachedAt: Date.now() });
3625
- }
3626
- /** Records that a non-spending permission was just granted so we can skip re-prompting briefly. */
3627
- markRecentGrant(request) {
3628
- if (request.type === 'spending')
3629
- return;
3630
- const key = this.buildRequestKey(request);
3631
- if (!key)
3632
- return;
3633
- this.recentGrants.set(key, Date.now() + WalletPermissionsManager.RECENT_GRANT_COVER_MS);
3634
- }
3635
- /** Returns true if we are inside the short "cover window" immediately after granting permission. */
3636
- isRecentlyGranted(key) {
3637
- const expiry = this.recentGrants.get(key);
3638
- if (!expiry)
3639
- return false;
3640
- if (Date.now() > expiry) {
3641
- this.recentGrants.delete(key);
3642
- return false;
3643
- }
3644
- return true;
3645
- }
3646
- /** Normalizes and canonicalizes originator domains (e.g., lowercase + drop default ports). */
3647
- normalizeOriginator(originator) {
3648
- if (!originator)
3649
- return '';
3650
- const trimmed = originator.trim();
3651
- if (!trimmed) {
3652
- return '';
3653
- }
3654
- try {
3655
- const hasScheme = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(trimmed);
3656
- const candidate = hasScheme ? trimmed : `https://${trimmed}`;
3657
- const url = new URL(candidate);
3658
- if (!url.hostname) {
3659
- return trimmed.toLowerCase();
3660
- }
3661
- const hostname = url.hostname.toLowerCase();
3662
- const needsBrackets = hostname.includes(':');
3663
- const baseHost = needsBrackets ? `[${hostname}]` : hostname;
3664
- const port = url.port;
3665
- const defaultPort = WalletPermissionsManager.DEFAULT_PORTS[url.protocol];
3666
- if (port && defaultPort && port === defaultPort) {
3667
- return baseHost;
3668
- }
3669
- return port ? `${baseHost}:${port}` : baseHost;
3670
- }
3671
- catch {
3672
- // Fall back to a conservative lowercase trim if URL parsing fails.
3673
- return trimmed.toLowerCase();
3674
- }
3675
- }
3676
- isWhitelistedCounterpartyProtocol(counterparty, protocolID) {
3677
- const whitelist = this.config.whitelistedCounterparties;
3678
- if (whitelist == null)
3679
- return false;
3680
- if (!counterparty || counterparty === 'self' || counterparty === 'anyone')
3681
- return false;
3682
- const protocols = whitelist[counterparty] || whitelist[counterparty.toLowerCase()] || whitelist[counterparty.toUpperCase()];
3683
- if (!protocols || protocols.length === 0)
3684
- return false;
3685
- const protoName = (protocolID?.[1] ?? '').toString().toLowerCase();
3686
- if (!protoName)
3687
- return false;
3688
- return protocols.some(p => (p ?? '').toString().toLowerCase() === protoName);
3689
- }
3690
- /**
3691
- * Produces a normalized originator value along with the set of legacy
3692
- * representations that should be considered when searching for existing
3693
- * permission tokens (for backwards compatibility).
3694
- */
3695
- prepareOriginator(originator) {
3696
- const trimmed = originator?.trim();
3697
- if (!trimmed) {
3698
- throw new Error('Originator is required for permission checks.');
3699
- }
3700
- const normalized = this.normalizeOriginator(trimmed) || trimmed.toLowerCase();
3701
- const lookupValues = Array.from(new Set([trimmed, normalized])).filter(Boolean);
3702
- return { normalized, lookupValues };
3703
- }
3704
- /**
3705
- * Builds a unique list of originator variants that should be searched when
3706
- * looking up on-chain tokens (e.g., legacy raw + normalized forms).
3707
- */
3708
- buildOriginatorLookupValues(...origins) {
3709
- const variants = new Set();
3710
- for (const origin of origins) {
3711
- const trimmed = origin?.trim();
3712
- if (trimmed) {
3713
- variants.add(trimmed);
3714
- }
3715
- }
3716
- return Array.from(variants);
3717
- }
3718
- /**
3719
- * Builds a "map key" string so that identical requests (e.g. "protocol:domain:true:protoName:counterparty")
3720
- * do not produce multiple user prompts.
3721
- */
3722
- buildRequestKey(r) {
3723
- const normalizedOriginator = this.normalizeOriginator(r.originator);
3724
- switch (r.type) {
3725
- case 'protocol':
3726
- return `proto:${normalizedOriginator}:${!!r.privileged}:${r.protocolID?.join(',')}:${r.counterparty}`;
3727
- case 'basket':
3728
- return `basket:${normalizedOriginator}:${r.basket}`;
3729
- case 'certificate':
3730
- return `cert:${normalizedOriginator}:${!!r.privileged}:${r.certificate?.verifier}:${r.certificate?.certType}:${r.certificate?.fields.join('|')}`;
3731
- case 'spending':
3732
- return `spend:${normalizedOriginator}:${r.spending?.satoshis}`;
3733
- }
3734
- }
3735
- buildActiveRequestKey(r) {
3736
- const base = this.buildRequestKey(r);
3737
- if (r.type === 'protocol' || r.type === 'basket' || r.type === 'certificate') {
3738
- return `${base}:${r.usageType ?? ''}`;
3739
- }
3740
- return base;
3741
- }
3742
- }
3743
- exports.WalletPermissionsManager = WalletPermissionsManager;
3744
- //# sourceMappingURL=WalletPermissionsManager.js.map