@bsv/wallet-toolbox 1.3.24 → 1.3.26

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 (120) hide show
  1. package/mobile/out/src/monitor/Monitor.d.ts.map +1 -1
  2. package/mobile/out/src/monitor/Monitor.js +4 -0
  3. package/mobile/out/src/monitor/Monitor.js.map +1 -1
  4. package/mobile/out/src/monitor/tasks/TaskServiceCallHistory.d.ts +12 -0
  5. package/mobile/out/src/monitor/tasks/TaskServiceCallHistory.d.ts.map +1 -0
  6. package/mobile/out/src/monitor/tasks/TaskServiceCallHistory.js +23 -0
  7. package/mobile/out/src/monitor/tasks/TaskServiceCallHistory.js.map +1 -0
  8. package/mobile/out/src/sdk/WalletServices.interfaces.d.ts +102 -0
  9. package/mobile/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  10. package/mobile/out/src/sdk/WalletStorage.interfaces.d.ts +5 -0
  11. package/mobile/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  12. package/mobile/out/src/services/ServiceCollection.d.ts +49 -1
  13. package/mobile/out/src/services/ServiceCollection.d.ts.map +1 -1
  14. package/mobile/out/src/services/ServiceCollection.js +139 -2
  15. package/mobile/out/src/services/ServiceCollection.js.map +1 -1
  16. package/mobile/out/src/services/Services.d.ts +4 -2
  17. package/mobile/out/src/services/Services.d.ts.map +1 -1
  18. package/mobile/out/src/services/Services.js +168 -74
  19. package/mobile/out/src/services/Services.js.map +1 -1
  20. package/mobile/out/src/services/createDefaultWalletServicesOptions.d.ts +1 -0
  21. package/mobile/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
  22. package/mobile/out/src/services/createDefaultWalletServicesOptions.js +15 -1
  23. package/mobile/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
  24. package/mobile/out/src/services/providers/ARC.d.ts +3 -2
  25. package/mobile/out/src/services/providers/ARC.d.ts.map +1 -1
  26. package/mobile/out/src/services/providers/ARC.js +5 -4
  27. package/mobile/out/src/services/providers/ARC.js.map +1 -1
  28. package/mobile/out/src/signer/methods/internalizeAction.d.ts.map +1 -1
  29. package/mobile/out/src/signer/methods/internalizeAction.js +3 -13
  30. package/mobile/out/src/signer/methods/internalizeAction.js.map +1 -1
  31. package/mobile/out/src/storage/StorageProvider.d.ts +6 -1
  32. package/mobile/out/src/storage/StorageProvider.d.ts.map +1 -1
  33. package/mobile/out/src/storage/StorageProvider.js +1 -1
  34. package/mobile/out/src/storage/StorageProvider.js.map +1 -1
  35. package/mobile/out/src/storage/methods/createAction.d.ts.map +1 -1
  36. package/mobile/out/src/storage/methods/createAction.js +5 -1
  37. package/mobile/out/src/storage/methods/createAction.js.map +1 -1
  38. package/mobile/out/src/storage/methods/internalizeAction.js +2 -1
  39. package/mobile/out/src/storage/methods/internalizeAction.js.map +1 -1
  40. package/mobile/package-lock.json +7 -6
  41. package/mobile/package.json +2 -2
  42. package/out/src/monitor/Monitor.d.ts.map +1 -1
  43. package/out/src/monitor/Monitor.js +4 -0
  44. package/out/src/monitor/Monitor.js.map +1 -1
  45. package/out/src/monitor/tasks/TaskServiceCallHistory.d.ts +12 -0
  46. package/out/src/monitor/tasks/TaskServiceCallHistory.d.ts.map +1 -0
  47. package/out/src/monitor/tasks/TaskServiceCallHistory.js +23 -0
  48. package/out/src/monitor/tasks/TaskServiceCallHistory.js.map +1 -0
  49. package/out/src/sdk/WalletServices.interfaces.d.ts +102 -0
  50. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  51. package/out/src/sdk/WalletStorage.interfaces.d.ts +5 -0
  52. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  53. package/out/src/services/ServiceCollection.d.ts +49 -1
  54. package/out/src/services/ServiceCollection.d.ts.map +1 -1
  55. package/out/src/services/ServiceCollection.js +139 -2
  56. package/out/src/services/ServiceCollection.js.map +1 -1
  57. package/out/src/services/Services.d.ts +4 -2
  58. package/out/src/services/Services.d.ts.map +1 -1
  59. package/out/src/services/Services.js +168 -74
  60. package/out/src/services/Services.js.map +1 -1
  61. package/out/src/services/__tests/ArcGorillaPool.man.test.d.ts +2 -0
  62. package/out/src/services/__tests/ArcGorillaPool.man.test.d.ts.map +1 -0
  63. package/out/src/services/__tests/ArcGorillaPool.man.test.js +93 -0
  64. package/out/src/services/__tests/ArcGorillaPool.man.test.js.map +1 -0
  65. package/out/src/services/createDefaultWalletServicesOptions.d.ts +1 -0
  66. package/out/src/services/createDefaultWalletServicesOptions.d.ts.map +1 -1
  67. package/out/src/services/createDefaultWalletServicesOptions.js +15 -1
  68. package/out/src/services/createDefaultWalletServicesOptions.js.map +1 -1
  69. package/out/src/services/providers/ARC.d.ts +3 -2
  70. package/out/src/services/providers/ARC.d.ts.map +1 -1
  71. package/out/src/services/providers/ARC.js +5 -4
  72. package/out/src/services/providers/ARC.js.map +1 -1
  73. package/out/src/signer/methods/internalizeAction.d.ts.map +1 -1
  74. package/out/src/signer/methods/internalizeAction.js +3 -13
  75. package/out/src/signer/methods/internalizeAction.js.map +1 -1
  76. package/out/src/storage/StorageKnex.d.ts +2 -2
  77. package/out/src/storage/StorageKnex.d.ts.map +1 -1
  78. package/out/src/storage/StorageKnex.js +55 -2
  79. package/out/src/storage/StorageKnex.js.map +1 -1
  80. package/out/src/storage/StorageProvider.d.ts +6 -1
  81. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  82. package/out/src/storage/StorageProvider.js +1 -1
  83. package/out/src/storage/StorageProvider.js.map +1 -1
  84. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  85. package/out/src/storage/methods/createAction.js +5 -1
  86. package/out/src/storage/methods/createAction.js.map +1 -1
  87. package/out/src/storage/methods/internalizeAction.js +2 -1
  88. package/out/src/storage/methods/internalizeAction.js.map +1 -1
  89. package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
  90. package/out/src/storage/schema/KnexMigrations.js +12 -0
  91. package/out/src/storage/schema/KnexMigrations.js.map +1 -1
  92. package/out/src/storage/schema/entities/__tests/ProvenTxTests.test.js +11 -1
  93. package/out/src/storage/schema/entities/__tests/ProvenTxTests.test.js.map +1 -1
  94. package/out/test/Wallet/local/localWallet.man.test.js +14 -16
  95. package/out/test/Wallet/local/localWallet.man.test.js.map +1 -1
  96. package/out/test/Wallet/support/operations.man.test.js +96 -6
  97. package/out/test/Wallet/support/operations.man.test.js.map +1 -1
  98. package/out/test/storage/KnexMigrations.test.js +1 -1
  99. package/out/test/storage/KnexMigrations.test.js.map +1 -1
  100. package/out/tsconfig.all.tsbuildinfo +1 -1
  101. package/package.json +2 -2
  102. package/src/monitor/Monitor.ts +4 -0
  103. package/src/monitor/tasks/TaskServiceCallHistory.ts +26 -0
  104. package/src/sdk/WalletServices.interfaces.ts +105 -0
  105. package/src/sdk/WalletStorage.interfaces.ts +5 -0
  106. package/src/services/ServiceCollection.ts +183 -2
  107. package/src/services/Services.ts +166 -76
  108. package/src/services/__tests/ArcGorillaPool.man.test.ts +108 -0
  109. package/src/services/createDefaultWalletServicesOptions.ts +16 -1
  110. package/src/services/providers/ARC.ts +8 -6
  111. package/src/signer/methods/internalizeAction.ts +4 -14
  112. package/src/storage/StorageKnex.ts +35 -4
  113. package/src/storage/StorageProvider.ts +8 -2
  114. package/src/storage/methods/createAction.ts +5 -3
  115. package/src/storage/methods/internalizeAction.ts +2 -1
  116. package/src/storage/schema/KnexMigrations.ts +13 -0
  117. package/src/storage/schema/entities/__tests/ProvenTxTests.test.ts +12 -1
  118. package/test/Wallet/local/localWallet.man.test.ts +15 -17
  119. package/test/Wallet/support/operations.man.test.ts +107 -7
  120. package/test/storage/KnexMigrations.test.ts +1 -1
@@ -8,6 +8,7 @@ import { updateChaintracksFiatExchangeRates, updateExchangeratesapi } from './pr
8
8
  import { ARC } from './providers/ARC'
9
9
  import { Bitails } from './providers/Bitails'
10
10
  import { getBeefForTxid } from './providers/getBeefForTxid'
11
+ import { ServicesCallHistory } from '../sdk/WalletServices.interfaces'
11
12
 
12
13
  export class Services implements sdk.WalletServices {
13
14
  static createDefaultOptions(chain: sdk.Chain): sdk.WalletServicesOptions {
@@ -16,7 +17,8 @@ export class Services implements sdk.WalletServices {
16
17
 
17
18
  options: sdk.WalletServicesOptions
18
19
  whatsonchain: WhatsOnChain
19
- arc: ARC
20
+ arcTaal: ARC
21
+ arcGorillaPool?: ARC
20
22
  bitails: Bitails
21
23
 
22
24
  getMerklePathServices: ServiceCollection<sdk.GetMerklePathService>
@@ -36,43 +38,65 @@ export class Services implements sdk.WalletServices {
36
38
 
37
39
  this.whatsonchain = new WhatsOnChain(this.chain, { apiKey: this.options.whatsOnChainApiKey }, this)
38
40
 
39
- this.arc = new ARC(this.options.arcUrl, this.options.arcConfig)
41
+ this.arcTaal = new ARC(this.options.arcUrl, this.options.arcConfig, 'arcTaal')
42
+ if (this.options.arcGorillaPoolUrl) {
43
+ //this.arcGorillaPool = new ARC(this.options.arcGorillaPoolUrl, this.options.arcGorillaPoolConfig, 'arcGorillaPool')
44
+ }
40
45
 
41
46
  this.bitails = new Bitails(this.chain)
42
47
 
43
48
  //prettier-ignore
44
- this.getMerklePathServices = new ServiceCollection<sdk.GetMerklePathService>()
49
+ this.getMerklePathServices = new ServiceCollection<sdk.GetMerklePathService>('getMerklePath')
45
50
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.getMerklePath.bind(this.whatsonchain) })
46
51
  .add({ name: 'Bitails', service: this.bitails.getMerklePath.bind(this.bitails) })
47
52
 
48
53
  //prettier-ignore
49
- this.getRawTxServices = new ServiceCollection<sdk.GetRawTxService>()
54
+ this.getRawTxServices = new ServiceCollection<sdk.GetRawTxService>('getRawTx')
50
55
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.getRawTxResult.bind(this.whatsonchain) })
51
56
 
57
+ this.postBeefServices = new ServiceCollection<sdk.PostBeefService>('postBeef')
58
+ if (this.arcGorillaPool) {
59
+ //prettier-ignore
60
+ this.postBeefServices.add({ name: 'GorillaPool', service: this.arcGorillaPool.postBeef.bind(this.arcGorillaPool) })
61
+ }
52
62
  //prettier-ignore
53
- this.postBeefServices = new ServiceCollection<sdk.PostBeefService>()
54
- .add({ name: 'TaalArcBeef', service: this.arc.postBeef.bind(this.arc) })
63
+ this.postBeefServices
64
+ .add({ name: 'TaalArcBeef', service: this.arcTaal.postBeef.bind(this.arcTaal) })
55
65
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.postBeef.bind(this.whatsonchain) })
56
66
  .add({ name: 'Bitails', service: this.bitails.postBeef.bind(this.bitails) })
67
+ ;
57
68
 
58
69
  //prettier-ignore
59
- this.getUtxoStatusServices = new ServiceCollection<sdk.GetUtxoStatusService>()
70
+ this.getUtxoStatusServices = new ServiceCollection<sdk.GetUtxoStatusService>('getUtxoStatus')
60
71
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.getUtxoStatus.bind(this.whatsonchain) })
61
72
 
62
73
  //prettier-ignore
63
- this.getStatusForTxidsServices = new ServiceCollection<sdk.GetStatusForTxidsService>()
74
+ this.getStatusForTxidsServices = new ServiceCollection<sdk.GetStatusForTxidsService>('getStatusForTxids')
64
75
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.getStatusForTxids.bind(this.whatsonchain) })
65
76
 
66
77
  //prettier-ignore
67
- this.getScriptHashHistoryServices = new ServiceCollection<sdk.GetScriptHashHistoryService>()
78
+ this.getScriptHashHistoryServices = new ServiceCollection<sdk.GetScriptHashHistoryService>('getScriptHashHistory')
68
79
  .add({ name: 'WhatsOnChain', service: this.whatsonchain.getScriptHashHistory.bind(this.whatsonchain) })
69
80
 
70
81
  //prettier-ignore
71
- this.updateFiatExchangeRateServices = new ServiceCollection<sdk.UpdateFiatExchangeRateService>()
82
+ this.updateFiatExchangeRateServices = new ServiceCollection<sdk.UpdateFiatExchangeRateService>('updateFiatExchangeRate')
72
83
  .add({ name: 'ChaintracksService', service: updateChaintracksFiatExchangeRates })
73
84
  .add({ name: 'exchangeratesapi', service: updateExchangeratesapi })
74
85
  }
75
86
 
87
+ getServicesCallHistory(reset?: boolean) : ServicesCallHistory {
88
+ return {
89
+ version: 2,
90
+ getMerklePath: this.getMerklePathServices.getServiceCallHistory(reset),
91
+ getRawTx: this.getRawTxServices.getServiceCallHistory(reset),
92
+ postBeef: this.postBeefServices.getServiceCallHistory(reset),
93
+ getUtxoStatus: this.getUtxoStatusServices.getServiceCallHistory(reset),
94
+ getStatusForTxids: this.getStatusForTxidsServices.getServiceCallHistory(reset),
95
+ getScriptHashHistory: this.getScriptHashHistoryServices.getServiceCallHistory(reset),
96
+ updateFiatExchangeRates: this.updateFiatExchangeRateServices.getServiceCallHistory(reset)
97
+ }
98
+ }
99
+
76
100
  async getChainTracker(): Promise<ChainTracker> {
77
101
  if (!this.options.chaintracks)
78
102
  throw new sdk.WERR_INVALID_PARAMETER('options.chaintracks', `valid to enable 'getChainTracker' service.`)
@@ -123,11 +147,22 @@ export class Services implements sdk.WalletServices {
123
147
  }
124
148
 
125
149
  for (let tries = 0; tries < services.count; tries++) {
126
- const service = services.service
127
- const r = await service(txids)
128
- if (r.status === 'success') {
129
- r0 = r
130
- break
150
+ const stc = services.serviceToCall
151
+ try {
152
+ const r = await stc.service(txids)
153
+ if (r.status === 'success') {
154
+ services.addServiceCallSuccess(stc)
155
+ r0 = r
156
+ break
157
+ } else {
158
+ if (r.error)
159
+ services.addServiceCallError(stc, r.error)
160
+ else
161
+ services.addServiceCallFailure(stc)
162
+ }
163
+ } catch (eu: unknown) {
164
+ const e = sdk.WalletError.fromUnknown(eu)
165
+ services.addServiceCallError(stc, e)
131
166
  }
132
167
  services.next()
133
168
  }
@@ -174,11 +209,22 @@ export class Services implements sdk.WalletServices {
174
209
 
175
210
  for (let retry = 0; retry < 2; retry++) {
176
211
  for (let tries = 0; tries < services.count; tries++) {
177
- const service = services.service
178
- const r = await service(output, outputFormat, outpoint)
179
- if (r.status === 'success') {
180
- r0 = r
181
- break
212
+ const stc = services.serviceToCall
213
+ try {
214
+ const r = await stc.service(output, outputFormat, outpoint)
215
+ if (r.status === 'success') {
216
+ services.addServiceCallSuccess(stc)
217
+ r0 = r
218
+ break
219
+ } else {
220
+ if (r.error)
221
+ services.addServiceCallError(stc, r.error)
222
+ else
223
+ services.addServiceCallFailure(stc)
224
+ }
225
+ } catch (eu: unknown) {
226
+ const e = sdk.WalletError.fromUnknown(eu)
227
+ services.addServiceCallError(stc, e)
182
228
  }
183
229
  services.next()
184
230
  }
@@ -200,19 +246,27 @@ export class Services implements sdk.WalletServices {
200
246
  }
201
247
 
202
248
  for (let tries = 0; tries < services.count; tries++) {
203
- const service = services.service
204
- const r = await service(hash)
205
- if (r.status === 'success') {
206
- r0 = r
207
- break
249
+ const stc = services.serviceToCall
250
+ try {
251
+ const r = await stc.service(hash)
252
+ if (r.status === 'success') {
253
+ r0 = r
254
+ break
255
+ } else {
256
+ if (r.error)
257
+ services.addServiceCallError(stc, r.error)
258
+ else
259
+ services.addServiceCallFailure(stc)
260
+ }
261
+ } catch (eu: unknown) {
262
+ const e = sdk.WalletError.fromUnknown(eu)
263
+ services.addServiceCallError(stc, e)
208
264
  }
209
265
  services.next()
210
266
  }
211
267
  return r0
212
268
  }
213
269
 
214
- postBeefCount = 0
215
-
216
270
  /**
217
271
  *
218
272
  * @param beef
@@ -220,15 +274,20 @@ export class Services implements sdk.WalletServices {
220
274
  * @returns
221
275
  */
222
276
  async postBeef(beef: Beef, txids: string[]): Promise<sdk.PostBeefResult[]> {
223
- this.postBeefCount++
224
- const services = [...this.postBeefServices.allServices]
225
- for (let i = this.postBeefCount % services.length; i > 0; i--) {
226
- // roll the array of services so the providers aren't always called in the same order.
227
- services.unshift(services.pop()!)
228
- }
277
+ const services = this.postBeefServices
278
+ const stcs = services.allServicesToCall
229
279
  let rs = await Promise.all(
230
- services.map(async service => {
231
- const r = await service(beef, txids)
280
+ stcs.map(async stc => {
281
+ const r = await stc.service(beef, txids)
282
+ if (r.status === 'success') {
283
+ services.addServiceCallSuccess(stc)
284
+ } else {
285
+ if (r.error) {
286
+ services.addServiceCallError(stc, r.error)
287
+ } else {
288
+ services.addServiceCallFailure(stc)
289
+ }
290
+ }
232
291
  return r
233
292
  })
234
293
  )
@@ -236,31 +295,46 @@ export class Services implements sdk.WalletServices {
236
295
  }
237
296
 
238
297
  async getRawTx(txid: string, useNext?: boolean): Promise<sdk.GetRawTxResult> {
239
- if (useNext) this.getRawTxServices.next()
298
+ const services = this.getRawTxServices
299
+ if (useNext) services.next()
240
300
 
241
301
  const r0: sdk.GetRawTxResult = { txid }
242
302
 
243
- for (let tries = 0; tries < this.getRawTxServices.count; tries++) {
244
- const service = this.getRawTxServices.service
245
- const r = await service(txid, this.chain)
246
- if (r.rawTx) {
247
- const hash = asString(doubleSha256BE(r.rawTx!))
248
- // Confirm transaction hash matches txid
249
- if (hash === asString(txid)) {
250
- // If we have a match, call it done.
251
- r0.rawTx = r.rawTx
252
- r0.name = r.name
253
- r0.error = undefined
254
- break
303
+ for (let tries = 0; tries < services.count; tries++) {
304
+ const stc = services.serviceToCall
305
+ try {
306
+ const r = await stc.service(txid, this.chain)
307
+ if (r.rawTx) {
308
+ const hash = asString(doubleSha256BE(r.rawTx!))
309
+ // Confirm transaction hash matches txid
310
+ if (hash === asString(txid)) {
311
+ // If we have a match, call it done.
312
+ r0.rawTx = r.rawTx
313
+ r0.name = r.name
314
+ r0.error = undefined
315
+ services.addServiceCallSuccess(stc)
316
+ break
317
+ }
318
+ r.error = new sdk.WERR_INTERNAL(`computed txid ${hash} doesn't match requested value ${txid}`)
319
+ r.rawTx = undefined
255
320
  }
256
- r.error = new sdk.WERR_INTERNAL(`computed txid ${hash} doesn't match requested value ${txid}`)
257
- r.rawTx = undefined
258
- }
259
- if (r.error && !r0.error && !r0.rawTx)
260
- // If we have an error and didn't before...
261
- r0.error = r.error
262
321
 
263
- this.getRawTxServices.next()
322
+ if (r.error)
323
+ services.addServiceCallError(stc, r.error)
324
+ else if (!r.rawTx)
325
+ services.addServiceCallSuccess(stc, `not found`)
326
+ else
327
+ services.addServiceCallFailure(stc)
328
+
329
+ if (r.error && !r0.error && !r0.rawTx)
330
+ // If we have an error and didn't before...
331
+ r0.error = r.error
332
+
333
+ } catch (eu: unknown) {
334
+ const e = sdk.WalletError.fromUnknown(eu)
335
+ services.addServiceCallError(stc, e)
336
+ }
337
+ services.next()
264
338
  }
265
339
  return r0
266
340
  }
@@ -307,28 +381,41 @@ export class Services implements sdk.WalletServices {
307
381
  }
308
382
 
309
383
  async getMerklePath(txid: string, useNext?: boolean): Promise<sdk.GetMerklePathResult> {
310
- if (useNext) this.getMerklePathServices.next()
384
+ const services = this.getMerklePathServices
385
+ if (useNext) services.next()
311
386
 
312
387
  const r0: sdk.GetMerklePathResult = { notes: [] }
313
388
 
314
- for (let tries = 0; tries < this.getMerklePathServices.count; tries++) {
315
- const service = this.getMerklePathServices.service
316
- const r = await service(txid, this)
317
- if (r.notes) r0.notes!.push(...r.notes)
318
- if (!r0.name) r0.name = r.name
319
- if (r.merklePath) {
320
- // If we have a proof, call it done.
321
- r0.merklePath = r.merklePath
322
- r0.header = r.header
323
- r0.name = r.name
324
- r0.error = undefined
325
- break
326
- } else if (r.error && !r0.error) {
327
- // If we have an error and didn't before...
328
- r0.error = r.error
389
+ for (let tries = 0; tries < services.count; tries++) {
390
+ const stc = services.serviceToCall
391
+ try {
392
+ const r = await stc.service(txid, this)
393
+ if (r.notes) r0.notes!.push(...r.notes)
394
+ if (!r0.name) r0.name = r.name
395
+ if (r.merklePath) {
396
+ // If we have a proof, call it done.
397
+ r0.merklePath = r.merklePath
398
+ r0.header = r.header
399
+ r0.name = r.name
400
+ r0.error = undefined
401
+ services.addServiceCallSuccess(stc)
402
+ break
403
+ }
404
+
405
+ if (r.error)
406
+ services.addServiceCallError(stc, r.error)
407
+ else
408
+ services.addServiceCallFailure(stc)
409
+
410
+ if (r.error && !r0.error) {
411
+ // If we have an error and didn't before...
412
+ r0.error = r.error
413
+ }
414
+ } catch (eu: unknown) {
415
+ const e = sdk.WalletError.fromUnknown(eu)
416
+ services.addServiceCallError(stc, e)
329
417
  }
330
-
331
- this.getMerklePathServices.next()
418
+ services.next()
332
419
  }
333
420
  return r0
334
421
  }
@@ -350,16 +437,19 @@ export class Services implements sdk.WalletServices {
350
437
  let r0: sdk.FiatExchangeRates | undefined
351
438
 
352
439
  for (let tries = 0; tries < services.count; tries++) {
353
- const service = services.service
440
+ const stc = services.serviceToCall
354
441
  try {
355
- const r = await service(this.targetCurrencies, this.options)
442
+ const r = await stc.service(this.targetCurrencies, this.options)
356
443
  if (this.targetCurrencies.every(c => typeof r.rates[c] === 'number')) {
444
+ services.addServiceCallSuccess(stc)
357
445
  r0 = r
358
446
  break
447
+ } else {
448
+ services.addServiceCallFailure(stc)
359
449
  }
360
450
  } catch (eu: unknown) {
361
451
  const e = sdk.WalletError.fromUnknown(eu)
362
- console.error(`updateFiatExchangeRates servcice name ${service.name} error ${e.message}`)
452
+ services.addServiceCallError(stc, e)
363
453
  }
364
454
  services.next()
365
455
  }
@@ -0,0 +1,108 @@
1
+ import { _tu, logger } from '../../../test/utils/TestUtilsWalletStorage'
2
+ import { sdk, wait } from '../../index.client'
3
+ import { ARC } from '../providers/ARC'
4
+ import { Beef, BeefTx } from '@bsv/sdk'
5
+ import { arcDefaultUrl, arcGorillaPoolUrl } from '../createDefaultWalletServicesOptions'
6
+ import { Setup } from '../../index.all'
7
+
8
+ describe('ArcGorillaPool tests', () => {
9
+ jest.setTimeout(99999999)
10
+
11
+ const env = _tu.getEnv('main')
12
+ const arc = new ARC(arcGorillaPoolUrl(env.chain)!, {
13
+ apiKey: ''
14
+ })
15
+
16
+ test.skip('0 double spend', async () => {
17
+ const beef = Beef.fromString(testnetDoubleSpendBeef)
18
+ const txids = [beef.txs.slice(-1)[0].txid]
19
+ const r = await arc.postBeef(beef, txids)
20
+ expect(r.status === 'error').toBe(true)
21
+ expect(r.txidResults[0].doubleSpend).toBe(true)
22
+ })
23
+
24
+ test.skip('1 postRawTx', async () => {
25
+ const r = await postRawTxTest('main', arc)
26
+ logger(`2 postBeef mainnet done ${r}`)
27
+ })
28
+
29
+ test('2 postBeef', async () => {
30
+ const r = await postBeefTest('main', arc)
31
+ logger(`2 postBeef mainnet done ${r}`)
32
+ })
33
+ })
34
+
35
+ async function postBeefTest(chain: sdk.Chain, arc: ARC): Promise<string> {
36
+ if (Setup.noEnv(chain)) return 'skipped'
37
+ const c = await _tu.createNoSendTxPair(chain)
38
+
39
+ const txids = [c.txidDo, c.txidUndo]
40
+
41
+ const r = await arc.postBeef(c.beef, txids)
42
+ expect(r.status).toBe('success')
43
+ for (const txid of txids) {
44
+ const tr = r.txidResults.find(tx => tx.txid === txid)
45
+ expect(tr).not.toBeUndefined()
46
+ expect(tr!.status).toBe('success')
47
+ }
48
+
49
+ // replace Undo transaction with double spend transaction and send again.
50
+ const beef2 = c.beef.clone()
51
+ beef2.txs[beef2.txs.length - 1] = BeefTx.fromTx(c.doubleSpendTx)
52
+ const txids2 = [c.txidDo, c.doubleSpendTx.id('hex')]
53
+
54
+ const r2 = await arc.postBeef(beef2, txids2)
55
+ expect(r2.status).toBe('error')
56
+ for (const txid of txids2) {
57
+ const tr = r2.txidResults.find(tx => tx.txid === txid)
58
+ expect(tr).not.toBeUndefined()
59
+ if (txid === c.txidDo) {
60
+ expect(tr!.status).toBe('success')
61
+ } else {
62
+ expect(tr!.status).toBe('error')
63
+ expect(tr!.doubleSpend).toBe(true)
64
+ expect(tr!.competingTxs).toEqual([c.txidUndo])
65
+ }
66
+ }
67
+ return 'passed'
68
+ }
69
+
70
+ async function postRawTxTest(chain: sdk.Chain, arc: ARC): Promise<void> {
71
+ if (Setup.noEnv(chain)) return
72
+ const c = await _tu.createNoSendTxPair(chain)
73
+
74
+ const rawTxDo = c.beef.findTxid(c.txidDo)!.tx!.toHex()
75
+ const rawTxUndo = c.beef.findTxid(c.txidUndo)!.tx!.toHex()
76
+
77
+ const rDo = await arc.postRawTx(rawTxDo)
78
+ expect(rDo.status).toBe('success')
79
+ expect(rDo.txid).toBe(c.txidDo)
80
+
81
+ await wait(1000)
82
+
83
+ const rUndo = await arc.postRawTx(rawTxUndo)
84
+ expect(rUndo.status).toBe('success')
85
+ expect(rUndo.txid).toBe(c.txidUndo)
86
+ expect(rUndo.doubleSpend).not.toBe(true)
87
+
88
+ await wait(1000)
89
+
90
+ {
91
+ // Send same transaction again...
92
+ const rUndo = await arc.postRawTx(rawTxUndo)
93
+ expect(rUndo.status).toBe('success')
94
+ expect(rUndo.txid).toBe(c.txidUndo)
95
+ expect(rUndo.doubleSpend).not.toBe(true)
96
+ }
97
+
98
+ await wait(1000)
99
+
100
+ // Confirm double spend detection.
101
+ const rDouble = await arc.postRawTx(c.doubleSpendTx.toHex())
102
+ expect(rDouble.status).toBe('error')
103
+ expect(rDouble.doubleSpend).toBe(true)
104
+ expect(rDouble.competingTxs![0]).toBe(c.txidUndo)
105
+ }
106
+
107
+ const testnetDoubleSpendBeef =
108
+ '0100beef01fe65631900020200009df812619ae232d2363d91516ab3e811211192933526bbc2aee71b54ccb236d10102462876eec65d9aa26d957421c5cc8dd9119b61177242b9dd814fb190fd0a361801010076a3297928f6841bcb656e91225540e87c65f67d8ec12bc768d7656eb7561b3d02010000000159617a9d17562f7c9765e5dfa6a9a393aa2809ca6166a3d7a31c09efcc5070141f0000006a47304402200a528145a67ba1879b88a093cb711f79f04413a81d5678f314302e36a7f59e43022010bc4bb3c2574052c50bbdc8a05c31fb39e69280656b34f5dc22e2ceadc3bb4a412102fd4200bf389d16479b3d06f97fee0752f2c3b9dc29fb3ddce2b327d851b8902bffffffff0204000000000000001976a9140df1a69c834bb7d9bb5b2b7d6a34e5a401db3e1688ac01000000000000001976a91423f2562a8092ed24eddc77c74387b44c561692a188ac0000000001000100000001462876eec65d9aa26d957421c5cc8dd9119b61177242b9dd814fb190fd0a3618000000006a47304402204183bbfdcf11d50907b91f5e70ea8f81228501ce84e24af75c8d984682d094dc022029caa8f7e5acb4990bbeafee523a3c4a99b78e98b9e5c41349147b099679d4ae412103b76389eea6494c2c30443cba9d59b9dba05fb04e467bc94272629615b87a429fffffffff0202000000000000001976a91476d851e59fcb4ee0ebe6947496db3a393b08e49c88ac01000000000000001976a91423f2562a8092ed24eddc77c74387b44c561692a188ac0000000000'
@@ -2,10 +2,15 @@ import { randomBytesHex, sdk } from '../index.client'
2
2
  import { ChaintracksServiceClient } from './chaintracker'
3
3
 
4
4
  export function createDefaultWalletServicesOptions(chain: sdk.Chain): sdk.WalletServicesOptions {
5
+ const deploymentId = `wallet-toolbox-${randomBytesHex(16)}`
5
6
  const taalApiKey =
6
7
  chain === 'main'
7
8
  ? 'mainnet_9596de07e92300c6287e4393594ae39c' // no plan
8
9
  : 'testnet_0e6cf72133b43ea2d7861da2a38684e3' // personal "starter" key
10
+ const gorillaPoolApiKey =
11
+ chain === 'main'
12
+ ? ''
13
+ : ''
9
14
 
10
15
  const o: sdk.WalletServicesOptions = {
11
16
  chain,
@@ -36,7 +41,12 @@ export function createDefaultWalletServicesOptions(chain: sdk.Chain): sdk.Wallet
36
41
  arcUrl: arcDefaultUrl(chain),
37
42
  arcConfig: {
38
43
  apiKey: taalApiKey,
39
- deploymentId: `wallet-toolbox-${randomBytesHex(16)}`
44
+ deploymentId
45
+ },
46
+ arcGorillaPoolUrl: arcGorillaPoolUrl(chain),
47
+ arcGorillaPoolConfig: {
48
+ apiKey: gorillaPoolApiKey,
49
+ deploymentId
40
50
  }
41
51
  }
42
52
  return o
@@ -46,3 +56,8 @@ export function arcDefaultUrl(chain: sdk.Chain): string {
46
56
  const url = chain === 'main' ? 'https://arc.taal.com' : 'https://arc-test.taal.com'
47
57
  return url
48
58
  }
59
+
60
+ export function arcGorillaPoolUrl(chain: sdk.Chain): string | undefined {
61
+ const url = chain === 'main' ? 'https://arc.gorillapool.io' : undefined
62
+ return url
63
+ }
@@ -36,6 +36,7 @@ function defaultDeploymentId(): string {
36
36
  * Represents an ARC transaction broadcaster.
37
37
  */
38
38
  export class ARC {
39
+ readonly name: string
39
40
  readonly URL: string
40
41
  readonly apiKey: string | undefined
41
42
  readonly deploymentId: string
@@ -50,16 +51,17 @@ export class ARC {
50
51
  * @param {string} URL - The URL endpoint for the ARC API.
51
52
  * @param {ArcConfig} config - Configuration options for the ARC broadcaster.
52
53
  */
53
- constructor(URL: string, config?: ArcConfig)
54
+ constructor(URL: string, config?: ArcConfig, name?: string)
54
55
  /**
55
56
  * Constructs an instance of the ARC broadcaster.
56
57
  *
57
58
  * @param {string} URL - The URL endpoint for the ARC API.
58
59
  * @param {string} apiKey - The API key used for authorization with the ARC API.
59
60
  */
60
- constructor(URL: string, apiKey?: string)
61
+ constructor(URL: string, apiKey?: string, name?: string)
61
62
 
62
- constructor(URL: string, config?: string | ArcConfig) {
63
+ constructor(URL: string, config?: string | ArcConfig, name?: string) {
64
+ this.name = name ?? 'ARC'
63
65
  this.URL = URL
64
66
  if (typeof config === 'string') {
65
67
  this.apiKey = config
@@ -143,7 +145,7 @@ export class ARC {
143
145
  }
144
146
 
145
147
  const url = `${this.URL}/v1/tx`
146
- const nn = () => ({ name: 'ARCv1tx', when: new Date().toISOString() })
148
+ const nn = () => ({ name: this.name, when: new Date().toISOString() })
147
149
  const nne = () => ({ ...nn(), rawTx, txids: txids.join(','), url })
148
150
 
149
151
  try {
@@ -241,13 +243,13 @@ export class ARC {
241
243
  */
242
244
  async postBeef(beef: Beef, txids: string[]): Promise<sdk.PostBeefResult> {
243
245
  const r: sdk.PostBeefResult = {
244
- name: 'ARC',
246
+ name: this.name,
245
247
  status: 'success',
246
248
  txidResults: [],
247
249
  notes: []
248
250
  }
249
251
 
250
- const nn = () => ({ name: 'ARCpostBeef', when: new Date().toISOString() })
252
+ const nn = () => ({ name: this.name, when: new Date().toISOString() })
251
253
 
252
254
  if (beef.version === BEEF_V2 && beef.txs.every(btx => !btx.isTxidOnly)) {
253
255
  beef.version = BEEF_V1
@@ -89,25 +89,15 @@ export async function internalizeAction(
89
89
  // TODO: Add support for known txids...
90
90
 
91
91
  const txValid = await ab.verify(await wallet.getServices().getChainTracker(), false)
92
- if (!txValid || !ab.atomicTxid) throw new sdk.WERR_INVALID_PARAMETER('tx', 'valid AtomicBEEF')
92
+ if (!txValid || !ab.atomicTxid) {
93
+ console.log(`internalizeAction beef is invalid: ${ab.toLogString()}`)
94
+ throw new sdk.WERR_INVALID_PARAMETER('tx', 'valid AtomicBEEF')
95
+ }
93
96
  const txid = ab.atomicTxid
94
97
  const btx = ab.findTxid(txid)
95
98
  if (!btx) throw new sdk.WERR_INVALID_PARAMETER('tx', `valid AtomicBEEF with newest txid of ${txid}`)
96
99
  const tx = btx.tx!
97
100
 
98
- /*
99
- for (const i of tx.inputs) {
100
- if (!i.sourceTXID)
101
- throw new sdk.WERR_INTERNAL('beef Transactions must have sourceTXIDs')
102
- if (!i.sourceTransaction) {
103
- const btx = ab.findTxid(i.sourceTXID)
104
- if (!btx)
105
- throw new sdk.WERR_INVALID_PARAMETER('tx', `valid AtomicBEEF and contain input transaction with txid ${i.sourceTXID}`);
106
- i.sourceTransaction = btx.tx
107
- }
108
- }
109
- */
110
-
111
101
  return { ab, tx, txid }
112
102
  }
113
103
  }
@@ -23,12 +23,13 @@ import {
23
23
  } from './schema/tables'
24
24
  import { KnexMigrations } from './schema/KnexMigrations'
25
25
  import { Knex } from 'knex'
26
- import { StorageAdminStats, StorageProvider, StorageProviderOptions } from './StorageProvider'
26
+ import { AdminStatsResult, StorageProvider, StorageProviderOptions } from './StorageProvider'
27
27
  import { purgeData } from './methods/purgeData'
28
28
  import { listActions } from './methods/listActionsKnex'
29
29
  import { listOutputs } from './methods/listOutputsKnex'
30
30
  import { DBType } from './StorageReader'
31
31
  import { reviewStatus } from './methods/reviewStatus'
32
+ import { ServicesCallHistory } from '../sdk/WalletServices.interfaces'
32
33
 
33
34
  export interface StorageKnexOptions extends StorageProviderOptions {
34
35
  /**
@@ -488,6 +489,27 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
488
489
  let q = this.toDb(args.trx)<T>(table)
489
490
  if (args.partial && Object.keys(args.partial).length > 0) q.where(args.partial)
490
491
  if (args.since) q.where('updated_at', '>=', this.validateDateForWhere(args.since))
492
+ if (args.orderDescending) {
493
+ let sortColumn = ''
494
+ switch (table) {
495
+ case 'certificates': sortColumn = 'certificateId'; break
496
+ case 'commissions': sortColumn = 'commissionId'; break
497
+ case 'output_baskets': sortColumn = 'basketId'; break
498
+ case 'outputs': sortColumn = 'outputId'; break
499
+ case 'output_tags': sortColumn = 'outputTagId'; break
500
+ case 'proven_tx_reqs': sortColumn = 'provenTxReqId'; break
501
+ case 'proven_txs': sortColumn = 'provenTxId'; break
502
+ case 'sync_states': sortColumn = 'syncStateId'; break
503
+ case 'transactions': sortColumn = 'transactionId'; break
504
+ case 'tx_labels': sortColumn = 'txLabelId'; break
505
+ case 'users': sortColumn = 'userId'; break
506
+ case 'monitor_events': sortColumn = 'id'; break
507
+ default: break;
508
+ }
509
+ if (sortColumn !== '') {
510
+ q.orderBy(sortColumn, 'desc')
511
+ }
512
+ }
491
513
  if (args.paged) {
492
514
  q.limit(args.paged.limit)
493
515
  q.offset(args.paged.offset || 0)
@@ -554,7 +576,10 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
554
576
  )
555
577
  const q = this.setupQuery('proven_tx_reqs', args)
556
578
  if (args.status && args.status.length > 0) q.whereIn('status', args.status)
557
- if (args.txids && args.txids.length > 0) q.whereIn('txid', args.txids)
579
+ if (args.txids) {
580
+ const txids = args.txids.filter(txid => txid !== undefined)
581
+ if (txids.length > 0) q.whereIn('txid', txids)
582
+ }
558
583
  return q
559
584
  }
560
585
  findProvenTxsQuery(args: sdk.FindProvenTxsArgs): Knex.QueryBuilder {
@@ -1128,9 +1153,13 @@ export class StorageKnex extends StorageProvider implements sdk.WalletStoragePro
1128
1153
  return entities
1129
1154
  }
1130
1155
 
1131
- async adminStats(adminIdentityKey: string): Promise<StorageAdminStats> {
1156
+ async adminStats(adminIdentityKey: string): Promise<AdminStatsResult> {
1132
1157
  if (this.dbtype !== 'MySQL') throw new sdk.WERR_NOT_IMPLEMENTED('adminStats, only MySQL is supported')
1133
1158
 
1159
+ const monitorEvent = verifyOneOrNone(await this.findMonitorEvents({ partial: { event: 'ServiceCallHistory'}, orderDescending: true, paged: { limit: 1 } }))
1160
+ const monitorStats: ServicesCallHistory | undefined = monitorEvent ? JSON.parse(monitorEvent.details!) : undefined
1161
+ const servicesStats = this.getServices().getServicesCallHistory(true)
1162
+
1134
1163
  const one_day_ago = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
1135
1164
  const one_week_ago = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()
1136
1165
  const one_month_ago = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
@@ -1271,7 +1300,9 @@ select
1271
1300
  (select count(*) from output_tags where created_at > '${one_month_ago}') as tagsMonth,
1272
1301
  (select count(*) from output_tags) as tagsTotal
1273
1302
  `)
1274
- const r: StorageAdminStats = {
1303
+ const r: AdminStatsResult = {
1304
+ monitorStats,
1305
+ servicesStats,
1275
1306
  requestedBy: adminIdentityKey,
1276
1307
  when: new Date().toISOString(),
1277
1308
  usersDay,