@bsv/wallet-toolbox-client 2.1.25 → 3.0.0-alpha.0

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 (146) hide show
  1. package/out/src/Wallet.d.ts.map +1 -1
  2. package/out/src/Wallet.js.map +1 -1
  3. package/out/src/WalletLogger.d.ts.map +1 -1
  4. package/out/src/WalletLogger.js.map +1 -1
  5. package/out/src/monitor/LeasedMonitorTask.d.ts +43 -0
  6. package/out/src/monitor/LeasedMonitorTask.d.ts.map +1 -0
  7. package/out/src/monitor/LeasedMonitorTask.js +89 -0
  8. package/out/src/monitor/LeasedMonitorTask.js.map +1 -0
  9. package/out/src/monitor/Monitor.d.ts +7 -0
  10. package/out/src/monitor/Monitor.d.ts.map +1 -1
  11. package/out/src/monitor/Monitor.js +7 -0
  12. package/out/src/monitor/Monitor.js.map +1 -1
  13. package/out/src/monitor/V7LeasedTask.d.ts +43 -0
  14. package/out/src/monitor/V7LeasedTask.d.ts.map +1 -0
  15. package/out/src/monitor/V7LeasedTask.js +89 -0
  16. package/out/src/monitor/V7LeasedTask.js.map +1 -0
  17. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts +2 -0
  18. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +1 -1
  19. package/out/src/monitor/tasks/TaskCheckForProofs.js +55 -0
  20. package/out/src/monitor/tasks/TaskCheckForProofs.js.map +1 -1
  21. package/out/src/monitor/tasks/TaskSendWaiting.d.ts.map +1 -1
  22. package/out/src/monitor/tasks/TaskSendWaiting.js.map +1 -1
  23. package/out/src/sdk/WalletStorage.interfaces.d.ts +59 -59
  24. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  25. package/out/src/sdk/types.d.ts +32 -0
  26. package/out/src/sdk/types.d.ts.map +1 -1
  27. package/out/src/sdk/types.js +50 -1
  28. package/out/src/sdk/types.js.map +1 -1
  29. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.d.ts.map +1 -1
  30. package/out/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.js.map +1 -1
  31. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.d.ts.map +1 -1
  32. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js.map +1 -1
  33. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.d.ts.map +1 -1
  34. package/out/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.js.map +1 -1
  35. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.d.ts.map +1 -1
  36. package/out/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.js.map +1 -1
  37. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.d.ts.map +1 -1
  38. package/out/src/services/chaintracker/chaintracks/createIdbChaintracks.js.map +1 -1
  39. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.d.ts.map +1 -1
  40. package/out/src/services/chaintracker/chaintracks/createNoDbChaintracks.js.map +1 -1
  41. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.d.ts.map +1 -1
  42. package/out/src/services/chaintracker/chaintracks/util/BulkFilesReader.js.map +1 -1
  43. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.d.ts.map +1 -1
  44. package/out/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.js.map +1 -1
  45. package/out/src/storage/StorageIdb.d.ts.map +1 -1
  46. package/out/src/storage/StorageIdb.js +10 -5
  47. package/out/src/storage/StorageIdb.js.map +1 -1
  48. package/out/src/storage/StorageProvider.d.ts +114 -1
  49. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  50. package/out/src/storage/StorageProvider.js +164 -4
  51. package/out/src/storage/StorageProvider.js.map +1 -1
  52. package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  53. package/out/src/storage/WalletStorageManager.js.map +1 -1
  54. package/out/src/storage/idbHelpers.d.ts +5 -0
  55. package/out/src/storage/idbHelpers.d.ts.map +1 -1
  56. package/out/src/storage/idbHelpers.js +42 -0
  57. package/out/src/storage/idbHelpers.js.map +1 -1
  58. package/out/src/storage/methods/attemptToPostReqsToNetwork.d.ts.map +1 -1
  59. package/out/src/storage/methods/attemptToPostReqsToNetwork.js +116 -4
  60. package/out/src/storage/methods/attemptToPostReqsToNetwork.js.map +1 -1
  61. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  62. package/out/src/storage/methods/createAction.js +22 -5
  63. package/out/src/storage/methods/createAction.js.map +1 -1
  64. package/out/src/storage/methods/internalizeAction.d.ts.map +1 -1
  65. package/out/src/storage/methods/internalizeAction.js +172 -5
  66. package/out/src/storage/methods/internalizeAction.js.map +1 -1
  67. package/out/src/storage/methods/processAction.d.ts.map +1 -1
  68. package/out/src/storage/methods/processAction.js +82 -14
  69. package/out/src/storage/methods/processAction.js.map +1 -1
  70. package/out/src/storage/schema/StorageIdbSchema.d.ts +43 -2
  71. package/out/src/storage/schema/StorageIdbSchema.d.ts.map +1 -1
  72. package/out/src/storage/schema/monitorLease.d.ts +57 -0
  73. package/out/src/storage/schema/monitorLease.d.ts.map +1 -0
  74. package/out/src/storage/schema/monitorLease.js +101 -0
  75. package/out/src/storage/schema/monitorLease.js.map +1 -0
  76. package/out/src/storage/schema/processingFsm.d.ts +27 -0
  77. package/out/src/storage/schema/processingFsm.d.ts.map +1 -0
  78. package/out/src/storage/schema/processingFsm.js +132 -0
  79. package/out/src/storage/schema/processingFsm.js.map +1 -0
  80. package/out/src/storage/schema/tables/TableAction.d.ts +38 -0
  81. package/out/src/storage/schema/tables/TableAction.d.ts.map +1 -0
  82. package/out/src/storage/schema/tables/TableAction.js +3 -0
  83. package/out/src/storage/schema/tables/TableAction.js.map +1 -0
  84. package/out/src/storage/schema/tables/TableChainTip.d.ts +17 -0
  85. package/out/src/storage/schema/tables/TableChainTip.d.ts.map +1 -0
  86. package/out/src/storage/schema/tables/TableChainTip.js +3 -0
  87. package/out/src/storage/schema/tables/TableChainTip.js.map +1 -0
  88. package/out/src/storage/schema/tables/TableMonitorLease.d.ts +23 -0
  89. package/out/src/storage/schema/tables/TableMonitorLease.d.ts.map +1 -0
  90. package/out/src/storage/schema/tables/TableMonitorLease.js +3 -0
  91. package/out/src/storage/schema/tables/TableMonitorLease.js.map +1 -0
  92. package/out/src/storage/schema/tables/TableOutput.d.ts +7 -0
  93. package/out/src/storage/schema/tables/TableOutput.d.ts.map +1 -1
  94. package/out/src/storage/schema/tables/TableOutput.js.map +1 -1
  95. package/out/src/storage/schema/tables/TableTransactionNew.d.ts +50 -0
  96. package/out/src/storage/schema/tables/TableTransactionNew.d.ts.map +1 -0
  97. package/out/src/storage/schema/tables/TableTransactionNew.js +3 -0
  98. package/out/src/storage/schema/tables/TableTransactionNew.js.map +1 -0
  99. package/out/src/storage/schema/tables/TableTransactionV7.d.ts +50 -0
  100. package/out/src/storage/schema/tables/TableTransactionV7.d.ts.map +1 -0
  101. package/out/src/storage/schema/tables/TableTransactionV7.js +3 -0
  102. package/out/src/storage/schema/tables/TableTransactionV7.js.map +1 -0
  103. package/out/src/storage/schema/tables/TableTxAudit.d.ts +26 -0
  104. package/out/src/storage/schema/tables/TableTxAudit.d.ts.map +1 -0
  105. package/out/src/storage/schema/tables/TableTxAudit.js +3 -0
  106. package/out/src/storage/schema/tables/TableTxAudit.js.map +1 -0
  107. package/out/src/storage/schema/tables/index.d.ts +5 -0
  108. package/out/src/storage/schema/tables/index.d.ts.map +1 -1
  109. package/out/src/storage/schema/tables/index.js +5 -0
  110. package/out/src/storage/schema/tables/index.js.map +1 -1
  111. package/out/src/storage/schema/transactionCrud.d.ts +41 -0
  112. package/out/src/storage/schema/transactionCrud.d.ts.map +1 -0
  113. package/out/src/storage/schema/transactionCrud.js +205 -0
  114. package/out/src/storage/schema/transactionCrud.js.map +1 -0
  115. package/out/src/storage/schema/transactionService.d.ts +315 -0
  116. package/out/src/storage/schema/transactionService.d.ts.map +1 -0
  117. package/out/src/storage/schema/transactionService.js +783 -0
  118. package/out/src/storage/schema/transactionService.js.map +1 -0
  119. package/out/src/storage/schema/txAudit.d.ts +33 -0
  120. package/out/src/storage/schema/txAudit.d.ts.map +1 -0
  121. package/out/src/storage/schema/txAudit.js +64 -0
  122. package/out/src/storage/schema/txAudit.js.map +1 -0
  123. package/out/src/storage/schema/v7Crud.d.ts +41 -0
  124. package/out/src/storage/schema/v7Crud.d.ts.map +1 -0
  125. package/out/src/storage/schema/v7Crud.js +205 -0
  126. package/out/src/storage/schema/v7Crud.js.map +1 -0
  127. package/out/src/storage/schema/v7Fsm.d.ts +27 -0
  128. package/out/src/storage/schema/v7Fsm.d.ts.map +1 -0
  129. package/out/src/storage/schema/v7Fsm.js +124 -0
  130. package/out/src/storage/schema/v7Fsm.js.map +1 -0
  131. package/out/src/storage/schema/v7MonitorLease.d.ts +57 -0
  132. package/out/src/storage/schema/v7MonitorLease.d.ts.map +1 -0
  133. package/out/src/storage/schema/v7MonitorLease.js +101 -0
  134. package/out/src/storage/schema/v7MonitorLease.js.map +1 -0
  135. package/out/src/storage/schema/v7Service.d.ts +305 -0
  136. package/out/src/storage/schema/v7Service.d.ts.map +1 -0
  137. package/out/src/storage/schema/v7Service.js +757 -0
  138. package/out/src/storage/schema/v7Service.js.map +1 -0
  139. package/out/src/storage/schema/v7TxAudit.d.ts +33 -0
  140. package/out/src/storage/schema/v7TxAudit.d.ts.map +1 -0
  141. package/out/src/storage/schema/v7TxAudit.js +64 -0
  142. package/out/src/storage/schema/v7TxAudit.js.map +1 -0
  143. package/out/src/storage/storageProviderHelpers.js +1 -1
  144. package/out/src/storage/storageProviderHelpers.js.map +1 -1
  145. package/out/tsconfig.client.tsbuildinfo +1 -1
  146. package/package.json +1 -1
@@ -0,0 +1,757 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.V7TransactionService = void 0;
4
+ const sdk_1 = require("@bsv/sdk");
5
+ const v7Crud_1 = require("./v7Crud");
6
+ const v7TxAudit_1 = require("./v7TxAudit");
7
+ const v7MonitorLease_1 = require("./v7MonitorLease");
8
+ /**
9
+ * High-level service over the V7 storage primitives.
10
+ *
11
+ * Storage methods and the Monitor call into this surface rather than the
12
+ * lower-level CRUD/FSM/audit/lease modules so that:
13
+ * - Every processing transition is audited.
14
+ * - Optimistic concurrency is uniformly enforced.
15
+ * - Chain tip + monitor lease access have one canonical entry point.
16
+ *
17
+ * Construction takes a Knex handle; instances are stateless and cheap to
18
+ * create — typically one per request or per Monitor task tick.
19
+ */
20
+ class V7TransactionService {
21
+ constructor(knex) {
22
+ this.knex = knex;
23
+ }
24
+ // -----------------------
25
+ // Transactions
26
+ // -----------------------
27
+ async findByTxid(txid) {
28
+ return await (0, v7Crud_1.findTransactionV7ByTxid)(this.knex, txid);
29
+ }
30
+ async findById(transactionId) {
31
+ return await (0, v7Crud_1.findTransactionV7)(this.knex, transactionId);
32
+ }
33
+ /**
34
+ * Insert a new V7 transaction row. The row is created in `queued` state
35
+ * unless the caller overrides `processing`.
36
+ */
37
+ async create(args) {
38
+ var _a, _b;
39
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
40
+ const row = {
41
+ txid: args.txid,
42
+ processing: (_b = args.processing) !== null && _b !== void 0 ? _b : 'queued',
43
+ processingChangedAt: now,
44
+ nextActionAt: undefined,
45
+ attempts: 0,
46
+ rebroadcastCycles: 0,
47
+ wasBroadcast: false,
48
+ idempotencyKey: args.idempotencyKey,
49
+ batch: args.batch,
50
+ rawTx: args.rawTx,
51
+ inputBeef: args.inputBeef,
52
+ height: undefined,
53
+ merkleIndex: undefined,
54
+ merklePath: undefined,
55
+ merkleRoot: undefined,
56
+ blockHash: undefined,
57
+ isCoinbase: args.isCoinbase === true,
58
+ lastProvider: undefined,
59
+ lastProviderStatus: undefined,
60
+ frozenReason: undefined,
61
+ rowVersion: 0
62
+ };
63
+ const id = await (0, v7Crud_1.insertTransactionV7)(this.knex, row, now);
64
+ await (0, v7TxAudit_1.auditProcessingTransition)(this.knex, id, row.processing, row.processing, { reason: 'create' }, now);
65
+ const stored = await this.findById(id);
66
+ if (stored == null)
67
+ throw new Error(`V7 transaction ${id} disappeared after insert`);
68
+ return stored;
69
+ }
70
+ /**
71
+ * Transition processing state with optimistic concurrency. Returns
72
+ * `undefined` when the FSM rejects the move OR the row's current state no
73
+ * longer matches `expectedFrom`.
74
+ */
75
+ async transition(args) {
76
+ return await (0, v7Crud_1.transitionProcessing)(this.knex, {
77
+ transactionId: args.transactionId,
78
+ expectedFromState: args.expectedFrom,
79
+ toState: args.to,
80
+ provider: args.provider,
81
+ providerStatus: args.providerStatus,
82
+ details: args.details
83
+ }, args.now);
84
+ }
85
+ /**
86
+ * Record acquisition of a Merkle proof for a transaction. Atomically:
87
+ * - Updates proof columns (height, index, merkle_path, merkle_root, block_hash)
88
+ * - Transitions processing to `proven` from any spendable-class state.
89
+ * - Writes a `proof.acquired` audit row.
90
+ */
91
+ async recordProof(args) {
92
+ var _a;
93
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
94
+ const next = await this.transition({
95
+ transactionId: args.transactionId,
96
+ expectedFrom: args.expectedFrom,
97
+ to: 'proven',
98
+ details: { source: 'recordProof', height: args.height },
99
+ now
100
+ });
101
+ if (next == null)
102
+ return undefined;
103
+ await this.knex('transactions').where({ transactionId: args.transactionId }).update({
104
+ height: args.height,
105
+ merkle_index: args.merkleIndex,
106
+ merkle_path: Buffer.from(args.merklePath),
107
+ merkle_root: args.merkleRoot,
108
+ block_hash: args.blockHash,
109
+ updated_at: now
110
+ });
111
+ return await this.findById(args.transactionId);
112
+ }
113
+ // -----------------------
114
+ // Actions
115
+ // -----------------------
116
+ async findActionForUser(userId, transactionId) {
117
+ return await (0, v7Crud_1.findAction)(this.knex, userId, transactionId);
118
+ }
119
+ async createAction(args) {
120
+ return await (0, v7Crud_1.insertAction)(this.knex, {
121
+ userId: args.userId,
122
+ transactionId: args.transactionId,
123
+ reference: args.reference,
124
+ description: args.description,
125
+ isOutgoing: args.isOutgoing,
126
+ satoshisDelta: args.satoshisDelta,
127
+ userNosend: args.userNosend === true,
128
+ hidden: false,
129
+ userAborted: false,
130
+ notifyJson: args.notifyJson,
131
+ rowVersion: 0
132
+ }, args.now);
133
+ }
134
+ // -----------------------
135
+ // Chain tip
136
+ // -----------------------
137
+ async getChainTip() {
138
+ const tip = await (0, v7Crud_1.getChainTip)(this.knex);
139
+ if (tip == null)
140
+ return undefined;
141
+ return { height: tip.height, blockHash: tip.blockHash };
142
+ }
143
+ async setChainTip(args) {
144
+ await (0, v7Crud_1.setChainTip)(this.knex, args, args.now);
145
+ }
146
+ // -----------------------
147
+ // Monitor lease
148
+ // -----------------------
149
+ async tryClaimLease(claim, now) {
150
+ return await (0, v7MonitorLease_1.tryClaimLease)(this.knex, claim, now);
151
+ }
152
+ async renewLease(renew, now) {
153
+ return await (0, v7MonitorLease_1.renewLease)(this.knex, renew, now);
154
+ }
155
+ async releaseLease(release) {
156
+ return await (0, v7MonitorLease_1.releaseLease)(this.knex, release);
157
+ }
158
+ // -----------------------
159
+ // Net-new methods (§3)
160
+ // -----------------------
161
+ /**
162
+ * #1 — Look up an action + its backing transaction by (userId, reference).
163
+ */
164
+ async findActionByReference(userId, reference) {
165
+ const actionRow = await this.knex('actions').where({ userId, reference }).first();
166
+ if (actionRow == null)
167
+ return undefined;
168
+ const action = (0, v7Crud_1.mapActionRow)(actionRow);
169
+ const tx = await this.findById(action.transactionId);
170
+ if (tx == null)
171
+ return undefined;
172
+ return { action, transaction: tx };
173
+ }
174
+ /**
175
+ * #2 — Look up an action + its backing transaction by (userId, txid).
176
+ */
177
+ async findActionByUserTxid(userId, txid) {
178
+ const txRow = await this.knex('transactions').where({ txid }).first();
179
+ if (txRow == null)
180
+ return undefined;
181
+ const tx = (0, v7Crud_1.mapTransactionRow)(txRow);
182
+ const actionRow = await this.knex('actions')
183
+ .where({ userId, transactionId: tx.transactionId })
184
+ .first();
185
+ if (actionRow == null)
186
+ return undefined;
187
+ return { action: (0, v7Crud_1.mapActionRow)(actionRow), transaction: tx };
188
+ }
189
+ /**
190
+ * #3 — Upsert: find existing action for (userId, txid) or create both the V7
191
+ * transaction row and the action row.
192
+ */
193
+ async findOrCreateActionForTxid(args) {
194
+ var _a, _b;
195
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
196
+ // Try to find existing transaction
197
+ let tx = await this.findByTxid(args.txid);
198
+ let isNew = false;
199
+ if (tx == null) {
200
+ tx = await this.create({
201
+ txid: args.txid,
202
+ processing: (_b = args.processing) !== null && _b !== void 0 ? _b : 'queued',
203
+ rawTx: args.rawTx,
204
+ inputBeef: args.inputBeef,
205
+ now
206
+ });
207
+ isNew = true;
208
+ }
209
+ else {
210
+ // Patch rawTx / inputBeef if the caller is supplying them for the first time.
211
+ const patches = { updated_at: now };
212
+ if (args.rawTx != null && tx.rawTx == null)
213
+ patches.raw_tx = Buffer.from(args.rawTx);
214
+ if (args.inputBeef != null && tx.inputBeef == null)
215
+ patches.input_beef = Buffer.from(args.inputBeef);
216
+ if (Object.keys(patches).length > 1) {
217
+ await this.knex('transactions').where({ transactionId: tx.transactionId }).update(patches);
218
+ const refreshed = await this.findById(tx.transactionId);
219
+ if (refreshed != null)
220
+ tx = refreshed;
221
+ }
222
+ }
223
+ // Find or create the action for this user.
224
+ let actionRow = await this.knex('actions')
225
+ .where({ userId: args.userId, transactionId: tx.transactionId })
226
+ .first();
227
+ if (actionRow == null) {
228
+ await (0, v7Crud_1.insertAction)(this.knex, {
229
+ userId: args.userId,
230
+ transactionId: tx.transactionId,
231
+ reference: args.reference,
232
+ description: args.description,
233
+ isOutgoing: args.isOutgoing,
234
+ satoshisDelta: args.satoshisDelta,
235
+ userNosend: false,
236
+ hidden: false,
237
+ userAborted: false,
238
+ rowVersion: 0
239
+ }, now);
240
+ actionRow = await this.knex('actions')
241
+ .where({ userId: args.userId, transactionId: tx.transactionId })
242
+ .first();
243
+ isNew = true;
244
+ }
245
+ return { action: (0, v7Crud_1.mapActionRow)(actionRow), transaction: tx, isNew };
246
+ }
247
+ /**
248
+ * #4 — Atomically update the satoshisDelta column on an action row.
249
+ */
250
+ async updateActionSatoshisDelta(actionId, delta, now) {
251
+ await this.knex('actions')
252
+ .where({ actionId })
253
+ .update({ satoshis_delta: delta, updated_at: now !== null && now !== void 0 ? now : new Date() });
254
+ }
255
+ /**
256
+ * #5 — Create a V7 transaction row already in `proven` state with all proof
257
+ * columns populated. Useful for internalised transactions that arrive with a
258
+ * Merkle proof (bump) already attached.
259
+ */
260
+ async createWithProof(args) {
261
+ var _a;
262
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
263
+ const row = {
264
+ txid: args.txid,
265
+ processing: 'proven',
266
+ processingChangedAt: now,
267
+ nextActionAt: undefined,
268
+ attempts: 0,
269
+ rebroadcastCycles: 0,
270
+ wasBroadcast: true,
271
+ idempotencyKey: undefined,
272
+ batch: undefined,
273
+ rawTx: args.rawTx,
274
+ inputBeef: args.inputBeef,
275
+ height: args.height,
276
+ merkleIndex: args.merkleIndex,
277
+ merklePath: args.merklePath,
278
+ merkleRoot: args.merkleRoot,
279
+ blockHash: args.blockHash,
280
+ isCoinbase: args.isCoinbase === true,
281
+ lastProvider: undefined,
282
+ lastProviderStatus: undefined,
283
+ frozenReason: undefined,
284
+ rowVersion: 0
285
+ };
286
+ const id = await (0, v7Crud_1.insertTransactionV7)(this.knex, row, now);
287
+ await (0, v7TxAudit_1.auditProcessingTransition)(this.knex, id, 'proven', 'proven', { reason: 'createWithProof' }, now);
288
+ const stored = await this.findById(id);
289
+ if (stored == null)
290
+ throw new Error(`V7 transaction ${id} disappeared after createWithProof insert`);
291
+ return stored;
292
+ }
293
+ /**
294
+ * #6 — Find an existing V7 transaction row for the given txid (suitable for
295
+ * the broadcast queue) or create a new one in `queued` state.
296
+ */
297
+ async findOrCreateForBroadcast(args) {
298
+ var _a, _b;
299
+ const existing = await this.findByTxid(args.txid);
300
+ if (existing != null) {
301
+ // Patch rawTx / batch if the row was previously created without them.
302
+ const patches = { updated_at: (_a = args.now) !== null && _a !== void 0 ? _a : new Date() };
303
+ if (existing.rawTx == null)
304
+ patches.raw_tx = Buffer.from(args.rawTx);
305
+ if (args.inputBeef != null && existing.inputBeef == null)
306
+ patches.input_beef = Buffer.from(args.inputBeef);
307
+ if (args.batch != null && existing.batch == null)
308
+ patches.batch = args.batch;
309
+ if (Object.keys(patches).length > 1) {
310
+ await this.knex('transactions').where({ transactionId: existing.transactionId }).update(patches);
311
+ const refreshed = await this.findById(existing.transactionId);
312
+ return { transaction: refreshed !== null && refreshed !== void 0 ? refreshed : existing, isNew: false };
313
+ }
314
+ return { transaction: existing, isNew: false };
315
+ }
316
+ const tx = await this.create({
317
+ txid: args.txid,
318
+ processing: (_b = args.processing) !== null && _b !== void 0 ? _b : 'queued',
319
+ rawTx: args.rawTx,
320
+ inputBeef: args.inputBeef,
321
+ batch: args.batch,
322
+ now: args.now
323
+ });
324
+ return { transaction: tx, isNew: true };
325
+ }
326
+ /**
327
+ * #7 — Bulk transition: attempt `transition` for each id; collect results.
328
+ * When `expectedFrom` is omitted the current state of each row is used as
329
+ * the expected source (lenient mode — only the FSM is checked).
330
+ */
331
+ async transitionMany(args) {
332
+ var _a;
333
+ const updated = [];
334
+ const skipped = [];
335
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
336
+ for (const id of args.transactionIds) {
337
+ let expectedFrom = args.expectedFrom;
338
+ if (expectedFrom == null) {
339
+ const row = await this.findById(id);
340
+ if (row == null) {
341
+ skipped.push(id);
342
+ continue;
343
+ }
344
+ expectedFrom = row.processing;
345
+ }
346
+ const result = await this.transition({
347
+ transactionId: id,
348
+ expectedFrom,
349
+ to: args.to,
350
+ provider: args.provider,
351
+ providerStatus: args.providerStatus,
352
+ details: args.details,
353
+ now
354
+ });
355
+ if (result != null) {
356
+ updated.push(id);
357
+ }
358
+ else {
359
+ skipped.push(id);
360
+ }
361
+ }
362
+ return { updated, skipped };
363
+ }
364
+ /**
365
+ * #8 — Bulk-set the `batch` column for a list of transaction ids.
366
+ * Pass `undefined` to clear the batch tag.
367
+ */
368
+ async setBatch(transactionIds, batch, now) {
369
+ if (transactionIds.length === 0)
370
+ return;
371
+ await this.knex('transactions')
372
+ .whereIn('transactionId', transactionIds)
373
+ .update({ batch: batch !== null && batch !== void 0 ? batch : null, updated_at: now !== null && now !== void 0 ? now : new Date() });
374
+ }
375
+ /**
376
+ * #9 — Atomically increment the `attempts` counter for one transaction and
377
+ * write an `attempts.incremented` audit entry.
378
+ */
379
+ async incrementAttempts(transactionId, now) {
380
+ const ts = now !== null && now !== void 0 ? now : new Date();
381
+ const updated = await this.knex('transactions')
382
+ .where({ transactionId })
383
+ .increment('attempts', 1)
384
+ .update({ updated_at: ts });
385
+ if (updated === 0)
386
+ return undefined;
387
+ await (0, v7TxAudit_1.appendTxAudit)(this.knex, { transactionId, event: 'attempts.incremented' }, ts);
388
+ return await this.findById(transactionId);
389
+ }
390
+ /**
391
+ * #10 — Record the outcome of a broadcast attempt. Transitions processing
392
+ * state, updates `wasBroadcast` and `lastProvider*` columns, and writes an
393
+ * audit row.
394
+ */
395
+ async recordBroadcastResult(args) {
396
+ var _a;
397
+ const now = (_a = args.now) !== null && _a !== void 0 ? _a : new Date();
398
+ const current = await this.findById(args.transactionId);
399
+ if (current == null)
400
+ return undefined;
401
+ const next = await this.transition({
402
+ transactionId: args.transactionId,
403
+ expectedFrom: current.processing,
404
+ to: args.status,
405
+ provider: args.provider,
406
+ providerStatus: args.providerStatus,
407
+ details: args.details,
408
+ now
409
+ });
410
+ if (next == null)
411
+ return undefined;
412
+ // Update wasBroadcast if the broadcast actually reached the network.
413
+ if (args.wasBroadcast === true && !next.wasBroadcast) {
414
+ await this.knex('transactions')
415
+ .where({ transactionId: args.transactionId })
416
+ .update({ was_broadcast: true, updated_at: now });
417
+ }
418
+ return await this.findById(args.transactionId);
419
+ }
420
+ /**
421
+ * #11 — Append a free-form history note to the audit log for a transaction.
422
+ */
423
+ async recordHistoryNote(transactionId, note, now) {
424
+ const { what, ...rest } = note;
425
+ await (0, v7TxAudit_1.appendTxAudit)(this.knex, {
426
+ transactionId,
427
+ event: 'history.note',
428
+ details: { what, ...rest }
429
+ }, now !== null && now !== void 0 ? now : new Date());
430
+ }
431
+ /**
432
+ * #12 — For each txid that exists in the V7 transactions table, merge the
433
+ * raw transaction bytes and (where available) the Merkle path into `beef`.
434
+ * Txids not present in V7 are silently skipped.
435
+ */
436
+ async mergeBeefForTxids(beef, txids) {
437
+ if (txids.length === 0)
438
+ return;
439
+ const rows = await this.knex('transactions')
440
+ .whereIn('txid', txids)
441
+ .select('txid', 'raw_tx', 'merkle_path');
442
+ for (const row of rows) {
443
+ if (row.raw_tx != null) {
444
+ const rawBytes = Array.from(row.raw_tx.values());
445
+ beef.mergeRawTx(rawBytes);
446
+ }
447
+ if (row.merkle_path != null) {
448
+ const mp = sdk_1.MerklePath.fromBinary(Array.from(row.merkle_path.values()));
449
+ beef.mergeBump(mp);
450
+ }
451
+ }
452
+ }
453
+ /**
454
+ * #13 — Collect broadcast-readiness info and a populated Beef for a list of
455
+ * txids. Each entry is classified as:
456
+ * - `readyToSend` — queued/sending → still needs broadcast
457
+ * - `alreadySent` — sent/seen/seen_multi/unconfirmed/proven → already on network
458
+ * - `error` — invalid/doubleSpend → terminal failure
459
+ * - `unknown` — not found in V7
460
+ */
461
+ async collectReqsAndBeef(txids, extraTxids) {
462
+ const beef = new sdk_1.Beef();
463
+ const details = [];
464
+ const allTxids = Array.from(new Set([...txids, ...(extraTxids !== null && extraTxids !== void 0 ? extraTxids : [])]));
465
+ if (allTxids.length > 0) {
466
+ const rows = await this.knex('transactions')
467
+ .whereIn('txid', allTxids)
468
+ .select('txid', 'processing', 'raw_tx', 'merkle_path');
469
+ const rowByTxid = new Map();
470
+ for (const row of rows)
471
+ rowByTxid.set(row.txid, row);
472
+ for (const txid of txids) {
473
+ const row = rowByTxid.get(txid);
474
+ if (row == null) {
475
+ details.push({ txid, status: 'unknown' });
476
+ continue;
477
+ }
478
+ const p = row.processing;
479
+ let status;
480
+ if (p === 'queued' || p === 'sending' || p === 'nonfinal') {
481
+ status = 'readyToSend';
482
+ }
483
+ else if (p === 'sent' || p === 'seen' || p === 'seen_multi' || p === 'unconfirmed' || p === 'proven') {
484
+ status = 'alreadySent';
485
+ }
486
+ else if (p === 'invalid' || p === 'doubleSpend') {
487
+ status = 'error';
488
+ }
489
+ else {
490
+ status = 'readyToSend';
491
+ }
492
+ details.push({ txid, status, reason: p });
493
+ // Merge raw tx + proof bytes for txids that have them.
494
+ if (row.raw_tx != null) {
495
+ beef.mergeRawTx(Array.from(row.raw_tx.values()));
496
+ }
497
+ if (row.merkle_path != null) {
498
+ const mp = sdk_1.MerklePath.fromBinary(Array.from(row.merkle_path.values()));
499
+ beef.mergeBump(mp);
500
+ }
501
+ }
502
+ // Also merge extra txids that may be input ancestors.
503
+ for (const txid of extraTxids !== null && extraTxids !== void 0 ? extraTxids : []) {
504
+ const row = rowByTxid.get(txid);
505
+ if (row == null)
506
+ continue;
507
+ if (row.raw_tx != null) {
508
+ beef.mergeRawTx(Array.from(row.raw_tx.values()));
509
+ }
510
+ if (row.merkle_path != null) {
511
+ const mp = sdk_1.MerklePath.fromBinary(Array.from(row.merkle_path.values()));
512
+ beef.mergeBump(mp);
513
+ }
514
+ }
515
+ }
516
+ return { beef, details };
517
+ }
518
+ /**
519
+ * #14 — Paginated list of actions (per-user transaction views) with optional
520
+ * status and label filters.
521
+ *
522
+ * After the V7 cutover `tx_labels_map.transactionId` references `actions.actionId`
523
+ * (not `transactions.transactionId`).
524
+ */
525
+ async listActionsForUser(args) {
526
+ let q = this.knex('actions as a')
527
+ .join('transactions as t', 't.transactionId', 'a.transactionId')
528
+ .where('a.userId', args.userId)
529
+ .where('a.hidden', false);
530
+ if (args.statusFilter != null && args.statusFilter.length > 0) {
531
+ q = q.whereIn('t.processing', args.statusFilter);
532
+ }
533
+ if (args.createdAtFrom != null) {
534
+ // IDB path uses >= for `from` (inclusive); mirror that here.
535
+ q = q.where('a.created_at', '>=', args.createdAtFrom);
536
+ }
537
+ if (args.createdAtTo != null) {
538
+ // IDB path uses EXCLUSIVE `to` semantics:
539
+ // r.created_at.getTime() >= args.to.getTime() → exclude
540
+ // Mirror that: exclude rows where created_at >= createdAtTo (use '<' not '<=').
541
+ q = q.where('a.created_at', '<', args.createdAtTo);
542
+ }
543
+ // Label filtering via tx_labels_map (post-cutover: transactionId = actionId)
544
+ if (args.labelIds != null && args.labelIds.length > 0) {
545
+ if (args.labelQueryMode === 'all') {
546
+ // Must have ALL specified labels
547
+ for (const labelId of args.labelIds) {
548
+ q = q.whereExists(this.knex('tx_labels_map as lm')
549
+ .where('lm.transactionId', this.knex.ref('a.actionId'))
550
+ .where('lm.txLabelId', labelId)
551
+ .whereNot('lm.isDeleted', true)
552
+ .select(this.knex.raw('1')));
553
+ }
554
+ }
555
+ else {
556
+ // Default: 'any' — must have at least one of the labels
557
+ q = q.whereExists(this.knex('tx_labels_map as lm')
558
+ .where('lm.transactionId', this.knex.ref('a.actionId'))
559
+ .whereIn('lm.txLabelId', args.labelIds)
560
+ .whereNot('lm.isDeleted', true)
561
+ .select(this.knex.raw('1')));
562
+ }
563
+ }
564
+ const countRow = await q.clone().count({ c: 'a.actionId' }).first();
565
+ const total = countRow != null ? Number(countRow.c) : undefined;
566
+ const rows = await q
567
+ .orderBy('a.created_at', 'desc')
568
+ .orderBy('a.actionId', 'asc')
569
+ .limit(args.limit)
570
+ .offset(args.offset)
571
+ .select('a.actionId', 'a.userId', 'a.transactionId', 'a.reference', 'a.description', 'a.isOutgoing', 'a.satoshis_delta', 'a.user_nosend', 'a.hidden', 'a.user_aborted', 'a.notify_json', 'a.row_version', 'a.created_at', 'a.updated_at', 't.txid', 't.processing', 't.height');
572
+ const mapped = rows.map((row) => {
573
+ var _a, _b;
574
+ return ({
575
+ actionId: row.actionId,
576
+ userId: row.userId,
577
+ transactionId: row.transactionId,
578
+ reference: row.reference,
579
+ description: row.description,
580
+ isOutgoing: !!row.isOutgoing,
581
+ satoshisDelta: row.satoshis_delta,
582
+ userNosend: !!row.user_nosend,
583
+ hidden: !!row.hidden,
584
+ userAborted: !!row.user_aborted,
585
+ notifyJson: (_a = row.notify_json) !== null && _a !== void 0 ? _a : undefined,
586
+ rowVersion: row.row_version,
587
+ created_at: new Date(row.created_at),
588
+ updated_at: new Date(row.updated_at),
589
+ txid: row.txid,
590
+ processing: row.processing,
591
+ height: (_b = row.height) !== null && _b !== void 0 ? _b : undefined
592
+ });
593
+ });
594
+ return { rows: mapped, total };
595
+ }
596
+ /**
597
+ * #15 — Paginated list of outputs with their backing transaction processing
598
+ * state. Optional filters: basket, tag set, processing state, spent flag.
599
+ */
600
+ async listOutputsForUser(args) {
601
+ let q = this.knex('outputs as o')
602
+ .join('transactions as t', 't.transactionId', 'o.transactionId')
603
+ .where('o.userId', args.userId);
604
+ if (args.processingFilter.length > 0) {
605
+ q = q.whereIn('t.processing', args.processingFilter);
606
+ }
607
+ if (!args.includeSpent) {
608
+ q = q.whereNull('o.spentBy');
609
+ }
610
+ if (args.basketId != null) {
611
+ q = q.where('o.basketId', args.basketId);
612
+ }
613
+ // Tag filtering via output_tags_map
614
+ if (args.tagIds != null && args.tagIds.length > 0) {
615
+ if (args.tagQueryMode === 'all') {
616
+ for (const tagId of args.tagIds) {
617
+ q = q.whereExists(this.knex('output_tags_map as otm')
618
+ .where('otm.outputId', this.knex.ref('o.outputId'))
619
+ .where('otm.outputTagId', tagId)
620
+ .whereNot('otm.isDeleted', true)
621
+ .select(this.knex.raw('1')));
622
+ }
623
+ }
624
+ else {
625
+ q = q.whereExists(this.knex('output_tags_map as otm')
626
+ .where('otm.outputId', this.knex.ref('o.outputId'))
627
+ .whereIn('otm.outputTagId', args.tagIds)
628
+ .whereNot('otm.isDeleted', true)
629
+ .select(this.knex.raw('1')));
630
+ }
631
+ }
632
+ const countRow = await q.clone().count({ c: 'o.outputId' }).first();
633
+ const total = countRow != null ? Number(countRow.c) : undefined;
634
+ const columns = [
635
+ 'o.outputId',
636
+ 'o.userId',
637
+ 'o.transactionId',
638
+ 'o.basketId',
639
+ 'o.spendable',
640
+ 'o.change',
641
+ 'o.outputDescription',
642
+ 'o.vout',
643
+ 'o.satoshis',
644
+ 'o.providedBy',
645
+ 'o.purpose',
646
+ 'o.type',
647
+ 'o.txid',
648
+ 'o.senderIdentityKey',
649
+ 'o.derivationPrefix',
650
+ 'o.derivationSuffix',
651
+ 'o.customInstructions',
652
+ 'o.spentBy',
653
+ 'o.sequenceNumber',
654
+ 'o.spendingDescription',
655
+ 'o.scriptLength',
656
+ 'o.scriptOffset',
657
+ 'o.created_at',
658
+ 'o.updated_at',
659
+ 't.processing'
660
+ ];
661
+ if (args.includeLockingScripts === true) {
662
+ columns.push('o.lockingScript');
663
+ }
664
+ const rows = await q
665
+ .orderBy('o.outputId', 'asc')
666
+ .limit(args.limit)
667
+ .offset(args.offset)
668
+ .select(columns);
669
+ const mapped = rows.map((row) => {
670
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
671
+ const out = {
672
+ outputId: row.outputId,
673
+ userId: row.userId,
674
+ transactionId: row.transactionId,
675
+ basketId: (_a = row.basketId) !== null && _a !== void 0 ? _a : undefined,
676
+ spendable: !!row.spendable,
677
+ change: !!row.change,
678
+ outputDescription: row.outputDescription,
679
+ vout: row.vout,
680
+ satoshis: row.satoshis,
681
+ providedBy: row.providedBy,
682
+ purpose: row.purpose,
683
+ type: row.type,
684
+ txid: (_b = row.txid) !== null && _b !== void 0 ? _b : undefined,
685
+ senderIdentityKey: (_c = row.senderIdentityKey) !== null && _c !== void 0 ? _c : undefined,
686
+ derivationPrefix: (_d = row.derivationPrefix) !== null && _d !== void 0 ? _d : undefined,
687
+ derivationSuffix: (_e = row.derivationSuffix) !== null && _e !== void 0 ? _e : undefined,
688
+ customInstructions: (_f = row.customInstructions) !== null && _f !== void 0 ? _f : undefined,
689
+ spentBy: (_g = row.spentBy) !== null && _g !== void 0 ? _g : undefined,
690
+ sequenceNumber: (_h = row.sequenceNumber) !== null && _h !== void 0 ? _h : undefined,
691
+ spendingDescription: (_j = row.spendingDescription) !== null && _j !== void 0 ? _j : undefined,
692
+ scriptLength: (_k = row.scriptLength) !== null && _k !== void 0 ? _k : undefined,
693
+ scriptOffset: (_l = row.scriptOffset) !== null && _l !== void 0 ? _l : undefined,
694
+ lockingScript: args.includeLockingScripts === true && row.lockingScript != null
695
+ ? Array.from(row.lockingScript.values())
696
+ : undefined,
697
+ created_at: new Date(row.created_at),
698
+ updated_at: new Date(row.updated_at),
699
+ processing: row.processing
700
+ };
701
+ return out;
702
+ });
703
+ return { rows: mapped, total };
704
+ }
705
+ /**
706
+ * Post-cutover helper: rewrite `tx_labels_map.transactionId` rows that were
707
+ * written with the legacy transactionId (before the real txid + actionId were
708
+ * known) so that they now point at the V7 `actions.actionId`.
709
+ *
710
+ * Call this once per new outgoing transaction immediately after
711
+ * `findOrCreateActionForTxid` resolves the actionId.
712
+ *
713
+ * This is a no-op when:
714
+ * - `legacyTransactionId` has no rows in `tx_labels_map` (no labels on the tx)
715
+ * - `legacyTransactionId === actionId` (should not happen in practice but
716
+ * is safe to call anyway)
717
+ */
718
+ async repointLabelsToActionId(legacyTransactionId, actionId, now) {
719
+ if (legacyTransactionId === actionId)
720
+ return;
721
+ const ts = now !== null && now !== void 0 ? now : new Date();
722
+ await this.knex('tx_labels_map')
723
+ .where({ transactionId: legacyTransactionId })
724
+ .update({ transactionId: actionId, updated_at: ts });
725
+ }
726
+ /**
727
+ * After `processAction` creates the V7 `transactions` row, remap
728
+ * `outputs.transactionId` and `outputs.spentBy` from the bridge-period
729
+ * `transactions_legacy.transactionId` to the real V7 `transactions.transactionId`.
730
+ *
731
+ * During `createAction`, new outputs are inserted with `transactionId =
732
+ * legacyTransactionId` (bypassing FK constraints). `listActionsKnex` queries
733
+ * outputs by V7 transactionId, so without this remap the outputs would be
734
+ * invisible to `listActions`.
735
+ *
736
+ * This is a no-op when `legacyTransactionId === v7TransactionId`.
737
+ */
738
+ async repointOutputsToV7TransactionId(legacyTransactionId, v7TransactionId, now) {
739
+ if (legacyTransactionId === v7TransactionId)
740
+ return;
741
+ const ts = now !== null && now !== void 0 ? now : new Date();
742
+ // Remap outputs.transactionId (the V7 FK for "which tx created this output")
743
+ await this.knex('outputs')
744
+ .where({ transactionId: legacyTransactionId })
745
+ .update({ transactionId: v7TransactionId, updated_at: ts });
746
+ // Remap outputs.spentBy (the V7 FK for "which tx spent this output")
747
+ await this.knex('outputs')
748
+ .where({ spentBy: legacyTransactionId })
749
+ .update({ spentBy: v7TransactionId, updated_at: ts });
750
+ // Remap commissions.transactionId
751
+ await this.knex('commissions')
752
+ .where({ transactionId: legacyTransactionId })
753
+ .update({ transactionId: v7TransactionId, updated_at: ts });
754
+ }
755
+ }
756
+ exports.V7TransactionService = V7TransactionService;
757
+ //# sourceMappingURL=v7Service.js.map