@bsv/wallet-toolbox 1.5.12 → 1.5.13

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 (58) hide show
  1. package/docs/client.md +180 -75
  2. package/docs/monitor.md +39 -2
  3. package/docs/wallet.md +180 -75
  4. package/mobile/out/src/monitor/Monitor.d.ts +23 -0
  5. package/mobile/out/src/monitor/Monitor.d.ts.map +1 -1
  6. package/mobile/out/src/monitor/Monitor.js +26 -16
  7. package/mobile/out/src/monitor/Monitor.js.map +1 -1
  8. package/mobile/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +1 -1
  9. package/mobile/out/src/monitor/tasks/TaskCheckForProofs.js +8 -0
  10. package/mobile/out/src/monitor/tasks/TaskCheckForProofs.js.map +1 -1
  11. package/mobile/out/src/monitor/tasks/TaskSendWaiting.d.ts.map +1 -1
  12. package/mobile/out/src/monitor/tasks/TaskSendWaiting.js +9 -0
  13. package/mobile/out/src/monitor/tasks/TaskSendWaiting.js.map +1 -1
  14. package/mobile/out/src/sdk/types.d.ts +11 -0
  15. package/mobile/out/src/sdk/types.d.ts.map +1 -1
  16. package/mobile/out/src/sdk/types.js.map +1 -1
  17. package/mobile/out/src/storage/methods/processAction.d.ts.map +1 -1
  18. package/mobile/out/src/storage/methods/processAction.js +6 -64
  19. package/mobile/out/src/storage/methods/processAction.js.map +1 -1
  20. package/mobile/out/src/utility/aggregateResults.d.ts +8 -0
  21. package/mobile/out/src/utility/aggregateResults.d.ts.map +1 -0
  22. package/mobile/out/src/utility/aggregateResults.js +59 -0
  23. package/mobile/out/src/utility/aggregateResults.js.map +1 -0
  24. package/mobile/package-lock.json +2 -2
  25. package/mobile/package.json +1 -1
  26. package/out/src/monitor/Monitor.d.ts +23 -0
  27. package/out/src/monitor/Monitor.d.ts.map +1 -1
  28. package/out/src/monitor/Monitor.js +26 -16
  29. package/out/src/monitor/Monitor.js.map +1 -1
  30. package/out/src/monitor/tasks/TaskCheckForProofs.d.ts.map +1 -1
  31. package/out/src/monitor/tasks/TaskCheckForProofs.js +8 -0
  32. package/out/src/monitor/tasks/TaskCheckForProofs.js.map +1 -1
  33. package/out/src/monitor/tasks/TaskSendWaiting.d.ts.map +1 -1
  34. package/out/src/monitor/tasks/TaskSendWaiting.js +9 -0
  35. package/out/src/monitor/tasks/TaskSendWaiting.js.map +1 -1
  36. package/out/src/sdk/types.d.ts +11 -0
  37. package/out/src/sdk/types.d.ts.map +1 -1
  38. package/out/src/sdk/types.js.map +1 -1
  39. package/out/src/services/__tests/ARC.timeout.man.test.js +1 -1
  40. package/out/src/storage/methods/processAction.d.ts.map +1 -1
  41. package/out/src/storage/methods/processAction.js +6 -64
  42. package/out/src/storage/methods/processAction.js.map +1 -1
  43. package/out/src/utility/aggregateResults.d.ts +8 -0
  44. package/out/src/utility/aggregateResults.d.ts.map +1 -0
  45. package/out/src/utility/aggregateResults.js +59 -0
  46. package/out/src/utility/aggregateResults.js.map +1 -0
  47. package/out/test/monitor/Monitor.test.js +101 -0
  48. package/out/test/monitor/Monitor.test.js.map +1 -1
  49. package/out/tsconfig.all.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/monitor/Monitor.ts +36 -15
  52. package/src/monitor/tasks/TaskCheckForProofs.ts +9 -0
  53. package/src/monitor/tasks/TaskSendWaiting.ts +12 -1
  54. package/src/sdk/types.ts +12 -0
  55. package/src/services/__tests/ARC.timeout.man.test.ts +1 -1
  56. package/src/storage/methods/processAction.ts +7 -78
  57. package/src/utility/aggregateResults.ts +68 -0
  58. package/test/monitor/Monitor.test.ts +123 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.5.12",
3
+ "version": "1.5.13",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
@@ -42,6 +42,12 @@ export interface MonitorOptions {
42
42
  unprovenAttemptsLimitTest: number
43
43
 
44
44
  unprovenAttemptsLimitMain: number
45
+
46
+ /**
47
+ * These are hooks for a wallet-toolbox client to get transaction updates.
48
+ */
49
+ onTransactionBroadcasted?: (broadcastResult: sdk.ReviewActionResult) => Promise<void>
50
+ onTransactionProven?: (txStatus: sdk.ProvenTransactionStatus) => Promise<void>
45
51
  }
46
52
 
47
53
  /**
@@ -75,6 +81,8 @@ export class Monitor {
75
81
  chain: sdk.Chain
76
82
  storage: MonitorStorage
77
83
  chaintracks: ChaintracksServiceClient
84
+ onTransactionBroadcasted?: (broadcastResult: sdk.ReviewActionResult) => Promise<void>
85
+ onTransactionProven?: (txStatus: sdk.ProvenTransactionStatus) => Promise<void>
78
86
 
79
87
  constructor(options: MonitorOptions) {
80
88
  this.options = { ...options }
@@ -82,6 +90,8 @@ export class Monitor {
82
90
  this.chain = this.services.chain
83
91
  this.storage = options.storage
84
92
  this.chaintracks = options.chaintracks
93
+ this.onTransactionProven = options.onTransactionProven
94
+ this.onTransactionBroadcasted = options.onTransactionBroadcasted
85
95
  }
86
96
 
87
97
  oneSecond = 1000
@@ -286,6 +296,32 @@ export class Monitor {
286
296
  TaskCheckForProofs.checkNow = true
287
297
  }
288
298
 
299
+ /**
300
+ * This is a function run from a TaskSendWaiting Monitor task.
301
+ *
302
+ * This allows the user of wallet-toolbox to 'subscribe' for transaction broadcast updates.
303
+ *
304
+ * @param broadcastResult
305
+ */
306
+ callOnBroadcastedTransaction(broadcastResult: sdk.ReviewActionResult): void {
307
+ if (this.onTransactionBroadcasted) {
308
+ this.onTransactionBroadcasted(broadcastResult)
309
+ }
310
+ }
311
+
312
+ /**
313
+ * This is a function run from a TaskCheckForProofs Monitor task.
314
+ *
315
+ * This allows the user of wallet-toolbox to 'subscribe' for transaction updates.
316
+ *
317
+ * @param txStatus
318
+ */
319
+ callOnProvenTransaction(txStatus: sdk.ProvenTransactionStatus): void {
320
+ if (this.onTransactionProven) {
321
+ this.onTransactionProven(txStatus)
322
+ }
323
+ }
324
+
289
325
  /**
290
326
  * Process reorg event received from Chaintracks
291
327
  *
@@ -301,18 +337,3 @@ export class Monitor {
301
337
  /* */
302
338
  }
303
339
  }
304
-
305
- function sum<T>(a: T[], getNum: (v: T) => number): number {
306
- let s = 0
307
- for (const v of a) s += getNum(v)
308
- return s
309
- }
310
-
311
- function filter<T>(a: T[], pred: (v: T) => boolean): { ts: T[]; fs: T[] } {
312
- const ts: T[] = []
313
- const fs: T[] = []
314
- for (const v of a)
315
- if (pred(v)) ts.push(v)
316
- else fs.push(v)
317
- return { ts, fs }
318
- }
@@ -216,6 +216,15 @@ export async function getProofs(
216
216
  req.apiHistory = r.history
217
217
  req.provenTxId = r.provenTxId
218
218
  req.notified = true
219
+
220
+ task.monitor.callOnProvenTransaction({
221
+ txid,
222
+ txIndex: index,
223
+ blockHeight: height,
224
+ blockHash,
225
+ merklePath,
226
+ merkleRoot
227
+ })
219
228
  } else {
220
229
  if (countsAsAttempt && req.status !== 'nosend') {
221
230
  req.attempts++
@@ -3,7 +3,9 @@ import { verifyTruthy } from '../../utility/index.client'
3
3
  import { Monitor } from '../Monitor'
4
4
  import { WalletMonitorTask } from './WalletMonitorTask'
5
5
  import { attemptToPostReqsToNetwork } from '../../storage/methods/attemptToPostReqsToNetwork'
6
- import { ProvenTxReqStatus } from '../../sdk'
6
+ import { ProvenTxReqStatus, WERR_INTERNAL } from '../../sdk'
7
+ import { ReviewActionResult, SendWithResult } from '@bsv/sdk'
8
+ import { aggregateActionResults } from '../../utility/aggregateResults'
7
9
 
8
10
  export class TaskSendWaiting extends WalletMonitorTask {
9
11
  static taskName = 'SendWaiting'
@@ -103,6 +105,15 @@ export class TaskSendWaiting extends WalletMonitorTask {
103
105
  return attemptToPostReqsToNetwork(sp, reqs)
104
106
  })
105
107
 
108
+ if (this.monitor.onTransactionBroadcasted) {
109
+ const rar = await this.storage.runAsStorageProvider(async sp => {
110
+ const ars: SendWithResult[] = [{ txid: req.txid, status: 'sending' }]
111
+ const { rar } = await aggregateActionResults(sp, ars, r)
112
+ return rar
113
+ })
114
+ this.monitor.callOnBroadcastedTransaction(rar[0])
115
+ }
116
+
106
117
  log += r.log
107
118
  }
108
119
  return log
package/src/sdk/types.ts CHANGED
@@ -133,6 +133,18 @@ export type ReqHistoryNote = {
133
133
  [key: string]: boolean | string | number | undefined
134
134
  }
135
135
 
136
+ /**
137
+ * The transaction status that a client will receive when subscribing to transaction updates in the Monitor.
138
+ */
139
+ export interface ProvenTransactionStatus {
140
+ txid: string
141
+ txIndex: number
142
+ blockHeight: number
143
+ blockHash: string
144
+ merklePath: number[]
145
+ merkleRoot: string
146
+ }
147
+
136
148
  /**
137
149
  * `listOutputs` special operation basket name value.
138
150
  *
@@ -45,7 +45,7 @@ describe('ARC tests', () => {
45
45
 
46
46
  const headers: Record<string, string> = {
47
47
  'Content-Type': 'application/json',
48
- 'XDeployment-ID': 'wallet-toolbox-test11',
48
+ 'XDeployment-ID': 'wallet-toolbox-test11'
49
49
  //Authorization: `Bearer ${envMain.gorillaPoolApiKey}`
50
50
  }
51
51
 
@@ -7,9 +7,7 @@ import {
7
7
  parseTxScriptOffsets,
8
8
  randomBytesBase64,
9
9
  sdk,
10
- sha256Hash,
11
10
  stampLog,
12
- stampLogFormat,
13
11
  StorageProvider,
14
12
  TableCommission,
15
13
  TableOutput,
@@ -17,16 +15,14 @@ import {
17
15
  TableProvenTxReq,
18
16
  TableTransaction,
19
17
  TxScriptOffsets,
20
- validateStorageFeeModel,
21
18
  verifyId,
22
19
  verifyInteger,
23
- verifyNumber,
24
20
  verifyOne,
25
21
  verifyOneOrNone,
26
22
  verifyTruthy
27
23
  } from '../../index.client'
28
- import { ReviewActionResult, ProvenTxReqNonTerminalStatus, StorageGetBeefOptions } from '../../sdk'
29
- import { PostReqsToNetworkDetails } from './attemptToPostReqsToNetwork'
24
+ import { ReviewActionResult } from '../../sdk'
25
+ import { aggregateActionResults } from '../../utility/aggregateResults'
30
26
 
31
27
  export async function processAction(
32
28
  storage: StorageProvider,
@@ -122,15 +118,6 @@ export async function shareReqsWithWorld(
122
118
  // Collect what we know about these sendWith transaction txids from storage.
123
119
  const r = await storage.getReqsAndBeefToShareWithWorld(txids, [])
124
120
 
125
- // Initialize aggregate results for each txid
126
- const ars: {
127
- txid: string
128
- status: SendWithResultStatus
129
- getReq: GetReqsAndBeefDetail
130
- postReq?: PostReqsToNetworkDetails
131
- ndr?: ReviewActionResult
132
- }[] = []
133
-
134
121
  const readyToSendReqs: EntityProvenTxReq[] = []
135
122
  for (const getReq of r.details) {
136
123
  let status: SendWithResultStatus = 'failed'
@@ -139,9 +126,8 @@ export async function shareReqsWithWorld(
139
126
  status = 'sending'
140
127
  readyToSendReqs.push(new EntityProvenTxReq(getReq.req!))
141
128
  }
142
- ars.push({
129
+ swr.push({
143
130
  txid: getReq.txid,
144
- getReq,
145
131
  status
146
132
  })
147
133
  }
@@ -171,11 +157,11 @@ export async function shareReqsWithWorld(
171
157
  await storage.updateTransaction(transactionIds, { status: 'sending' }, trx)
172
158
  })
173
159
  }
174
- return createResults()
160
+ return { swr, ndr }
175
161
  }
176
162
 
177
163
  if (readyToSendReqIds.length < 1) {
178
- return createResults()
164
+ return { swr, ndr }
179
165
  }
180
166
 
181
167
  if (batch) {
@@ -189,53 +175,8 @@ export async function shareReqsWithWorld(
189
175
  //
190
176
  const prtn = await storage.attemptToPostReqsToNetwork(readyToSendReqs)
191
177
 
192
- // merge the individual PostBeefResultForTxid results to postBeef in aggregate results.
193
- for (const ar of ars) {
194
- const txid = ar.txid
195
- const d = prtn.details.find(d => d.txid === txid)
196
- if (!d) throw new sdk.WERR_INTERNAL(`missing details for ${txid}`)
197
- ar.ndr = { txid: d.txid, status: 'success', competingTxs: d.competingTxs }
198
- switch (d.status) {
199
- case 'success':
200
- // processing network has accepted this transaction
201
- ar.status = 'unproven'
202
- break
203
- case 'doubleSpend':
204
- // confirmed double spend.
205
- ar.status = 'failed'
206
- ar.ndr.status = 'doubleSpend'
207
- if (d.competingTxs) ar.ndr.competingBeef = await createMergedBeefOfTxids(d.competingTxs, storage)
208
- break
209
- case 'serviceError':
210
- // services might improve
211
- ar.status = 'sending'
212
- ar.ndr.status = 'serviceError'
213
- break
214
- case 'invalidTx':
215
- // nothing will fix this transaction
216
- ar.status = 'failed'
217
- ar.ndr.status = 'invalidTx'
218
- break
219
- case 'unknown':
220
- case 'invalid':
221
- default:
222
- throw new sdk.WERR_INTERNAL(`processAction with notDelayed status ${d.status} should not occur.`)
223
- }
224
- }
225
-
226
- return createResults()
227
-
228
- function createResults(): { swr: SendWithResult[]; ndr: ReviewActionResult[] | undefined } {
229
- swr = []
230
- ndr = isDelayed ? undefined : []
231
- for (const ar of ars) {
232
- swr.push({ txid: ar.txid, status: ar.status })
233
- if (ar.ndr && ndr) {
234
- ndr.push(ar.ndr)
235
- }
236
- }
237
- return { swr, ndr }
238
- }
178
+ const { swr: swrRes, rar } = await aggregateActionResults(storage, swr, prtn)
179
+ return { swr: swrRes, ndr: rar }
239
180
  }
240
181
 
241
182
  interface ReqTxStatus {
@@ -452,15 +393,3 @@ async function commitNewTxToStorage(
452
393
 
453
394
  return r
454
395
  }
455
-
456
- async function createMergedBeefOfTxids(txids: string[], storage: StorageProvider): Promise<number[]> {
457
- const beef = new Beef()
458
- const options: StorageGetBeefOptions = {
459
- mergeToBeef: beef,
460
- ignoreNewProven: true
461
- }
462
- for (const txid of txids) {
463
- await storage.getBeefForTransaction(txid, options)
464
- }
465
- return beef.toBinary()
466
- }
@@ -0,0 +1,68 @@
1
+ import { Beef, ReviewActionResult, SendWithResult } from '@bsv/sdk'
2
+ import { PostReqsToNetworkResult } from '../storage/methods/attemptToPostReqsToNetwork'
3
+ import { StorageGetBeefOptions, WERR_INTERNAL } from '../sdk'
4
+ import { StorageProvider } from '../index.client'
5
+
6
+ export const aggregateActionResults = async (
7
+ storage: StorageProvider,
8
+ sendWithResultReqs: SendWithResult[],
9
+ postToNetworkResult: PostReqsToNetworkResult
10
+ ): Promise<{
11
+ swr: SendWithResult[]
12
+ rar: ReviewActionResult[]
13
+ }> => {
14
+ const swr: SendWithResult[] = []
15
+ const rar: ReviewActionResult[] = []
16
+
17
+ for (const ar of sendWithResultReqs) {
18
+ const txid = ar.txid
19
+
20
+ const d = postToNetworkResult.details.find(d => d.txid === txid)
21
+ if (!d) throw new WERR_INTERNAL(`missing details for ${txid}`)
22
+
23
+ const arNdr: ReviewActionResult = { txid: d.txid, status: 'success', competingTxs: d.competingTxs }
24
+ switch (d.status) {
25
+ case 'success':
26
+ // processing network has accepted this transaction
27
+ ar.status = 'unproven'
28
+ break
29
+ case 'doubleSpend':
30
+ // confirmed double spend.
31
+ ar.status = 'failed'
32
+ arNdr.status = 'doubleSpend'
33
+ if (d.competingTxs) arNdr.competingBeef = await createMergedBeefOfTxids(d.competingTxs, storage)
34
+ break
35
+ case 'serviceError':
36
+ // services might improve
37
+ ar.status = 'sending'
38
+ arNdr.status = 'serviceError'
39
+ break
40
+ case 'invalidTx':
41
+ // nothing will fix this transaction
42
+ ar.status = 'failed'
43
+ arNdr.status = 'invalidTx'
44
+ break
45
+ case 'unknown':
46
+ case 'invalid':
47
+ default:
48
+ throw new WERR_INTERNAL(`processAction with notDelayed status ${d.status} should not occur.`)
49
+ }
50
+
51
+ swr.push({ txid, status: ar.status })
52
+ rar.push(arNdr)
53
+ }
54
+
55
+ return { swr, rar }
56
+ }
57
+
58
+ async function createMergedBeefOfTxids(txids: string[], storage: StorageProvider): Promise<number[]> {
59
+ const beef = new Beef()
60
+ const options: StorageGetBeefOptions = {
61
+ mergeToBeef: beef,
62
+ ignoreNewProven: true
63
+ }
64
+ for (const txid of txids) {
65
+ await storage.getBeefForTransaction(txid, options)
66
+ }
67
+ return beef.toBinary()
68
+ }
@@ -1,4 +1,4 @@
1
- import { MerklePath } from '@bsv/sdk'
1
+ import { Beef, MerklePath } from '@bsv/sdk'
2
2
  import { asArray, EntityProvenTxReq, sdk, verifyOne, verifyTruthy, wait } from '../../src/index.client'
3
3
  import { TaskCheckForProofs } from '../../src/monitor/tasks/TaskCheckForProofs'
4
4
  import { TaskClock } from '../../src/monitor/tasks/TaskClock'
@@ -490,4 +490,126 @@ describe('Monitor tests', () => {
490
490
  await ctx.storage.destroy()
491
491
  }
492
492
  })
493
+
494
+ test('8 ProcessProvenTransaction', async () => {
495
+ const ctxs: TestWallet<{}>[] = []
496
+ ctxs.push(await _tu.createLegacyWalletSQLiteCopy('monitorTest8'))
497
+ let mockResultIndex = 0
498
+ let updatesReceived = 0
499
+
500
+ const expectedTxids = [
501
+ 'c099c52277426abb863dc902d0389b008ddf2301d6b40ac718746ac16ca59136',
502
+ '6935ce33b9e3b9ee60360ce0606aa0a0970b4840203f457b5559212676dc33ab',
503
+ '67ca2475886b3fc2edd76a2eb8c32bd0bc308176c7dff463e0507942aeebcbec',
504
+ '3fa94b62a3b10d8c18bada527a9b68c4e70db67140719df16c44fb0328782532',
505
+ '519675259eff036c6597e4a497d37c132e718171dde4ea2257e84c947ecf656b'
506
+ ]
507
+
508
+ _tu.mockMerklePathServicesAsCallback(ctxs, async txid => {
509
+ expect(expectedTxids).toContain(txid)
510
+ const r = mockGetMerklePathResults[mockResultIndex++]
511
+ return r
512
+ })
513
+
514
+ for (const { activeStorage: storage, monitor } of ctxs) {
515
+ if (!monitor) throw new sdk.WERR_INTERNAL('test requires setup with monitor')
516
+
517
+ monitor.lastNewHeader = {
518
+ height: 999999999,
519
+ hash: '',
520
+ time: 0,
521
+ version: 0,
522
+ previousHash: '',
523
+ merkleRoot: '',
524
+ bits: 0,
525
+ nonce: 0
526
+ }
527
+
528
+ monitor.onTransactionProven = async (txStatus: sdk.ProvenTransactionStatus) => {
529
+ expect(txStatus.txid).toBeTruthy()
530
+ expect(txStatus.blockHash).toBeTruthy()
531
+ expect(txStatus.blockHeight).toBeTruthy()
532
+ expect(txStatus.merkleRoot).toBeTruthy()
533
+ updatesReceived++
534
+ }
535
+
536
+ for (const txid of expectedTxids) {
537
+ // no matching ProvenTx exists.
538
+ expect((await storage.findProvenTxs({ partial: { txid } })).length).toBe(0)
539
+ const req = verifyTruthy(await EntityProvenTxReq.fromStorageTxid(storage, txid))
540
+ expect(req.status).toBe('unmined')
541
+ }
542
+
543
+ const task = new TaskCheckForProofs(monitor, 1)
544
+ monitor._tasks.push(task)
545
+
546
+ await monitor.runTask('CheckForProofs')
547
+
548
+ for (const txid of expectedTxids) {
549
+ const proven = verifyOne(await storage.findProvenTxs({ partial: { txid } }))
550
+ expect(proven.merklePath).toBeTruthy()
551
+ const req = verifyTruthy(await EntityProvenTxReq.fromStorageTxid(storage, txid))
552
+ expect(req.status).toBe('completed')
553
+ expect(req.provenTxId).toBe(proven.provenTxId)
554
+ }
555
+
556
+ expect(updatesReceived).toEqual(expectedTxids.length)
557
+ }
558
+
559
+ for (const ctx of ctxs) {
560
+ await ctx.storage.destroy()
561
+ }
562
+ })
563
+
564
+ test('9 ProcessBroadcastedTransactions', async () => {
565
+ const ctxs: TestWallet<{}>[] = []
566
+ ctxs.push(await _tu.createLegacyWalletSQLiteCopy('monitorTest8'))
567
+ let updatesReceived = 0
568
+ let txidsPosted: string[] = []
569
+
570
+ const expectedTxids = [
571
+ 'd9ec73b2e0f06e0f482d2d1db9ceccf2f212f0b24afbe10846ac907567be571f',
572
+ 'b7634f08d8c7f3c6244050bebf73a79f40e672aba7d5232663609a58b123b816',
573
+ '3d2ea64ee584a1f6eb161dbedf3a8d299e3e4497ac7a203d23c044c998c6aa08',
574
+ 'a3a8fe7f541c1383ff7b975af49b27284ae720af5f2705d8409baaf519190d26',
575
+ '6d68cc6fa7363e59aaccbaa65f0ca613a6ae8af718453ab5d3a2b022c59b5cc6'
576
+ ]
577
+
578
+ _tu.mockPostServicesAsCallback(ctxs, (beef: Beef, txids: string[]) => {
579
+ txidsPosted.push(...txids)
580
+ return 'success'
581
+ })
582
+
583
+ for (const { activeStorage: storage, monitor } of ctxs) {
584
+ if (!monitor) throw new sdk.WERR_INTERNAL('test requires setup with monitor')
585
+
586
+ for (const txid of expectedTxids) {
587
+ const req = verifyTruthy(await EntityProvenTxReq.fromStorageTxid(storage, txid))
588
+ expect(req.status).toBe('unsent')
589
+ }
590
+
591
+ monitor.onTransactionBroadcasted = async (broadcastResult: sdk.ReviewActionResult) => {
592
+ expect(broadcastResult.status).toBe('success')
593
+ expect(expectedTxids).toContain(broadcastResult.txid)
594
+ updatesReceived++
595
+ }
596
+
597
+ const task = new TaskSendWaiting(monitor, 1, 1)
598
+ monitor._tasks.push(task)
599
+
600
+ await monitor.runTask('SendWaiting')
601
+
602
+ expect(txidsPosted).toEqual(expectedTxids)
603
+ for (const txid of expectedTxids) {
604
+ const req = verifyOne(await storage.findProvenTxReqs({ partial: { txid } }))
605
+ expect(req.status).toBe('unmined')
606
+ }
607
+
608
+ expect(updatesReceived).toEqual(expectedTxids.length)
609
+ }
610
+
611
+ for (const ctx of ctxs) {
612
+ await ctx.storage.destroy()
613
+ }
614
+ })
493
615
  })