@bsv/wallet-toolbox 1.3.23 → 1.3.24

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 (184) hide show
  1. package/docs/client.md +99 -59
  2. package/docs/storage.md +55 -75
  3. package/docs/wallet.md +99 -59
  4. package/mobile/out/src/Wallet.d.ts +1 -1
  5. package/mobile/out/src/Wallet.d.ts.map +1 -1
  6. package/mobile/out/src/Wallet.js +16 -6
  7. package/mobile/out/src/Wallet.js.map +1 -1
  8. package/mobile/out/src/monitor/Monitor.d.ts.map +1 -1
  9. package/mobile/out/src/monitor/Monitor.js +2 -4
  10. package/mobile/out/src/monitor/Monitor.js.map +1 -1
  11. package/mobile/out/src/monitor/tasks/TaskNewHeader.d.ts +1 -1
  12. package/mobile/out/src/monitor/tasks/TaskNewHeader.d.ts.map +1 -1
  13. package/mobile/out/src/sdk/WalletStorage.interfaces.d.ts +14 -1
  14. package/mobile/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  15. package/mobile/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.d.ts.map +1 -0
  16. package/mobile/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.js.map +1 -0
  17. package/mobile/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts +1 -1
  18. package/mobile/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts.map +1 -1
  19. package/mobile/out/src/services/chaintracker/chaintracks/index.d.ts +1 -1
  20. package/mobile/out/src/services/chaintracker/chaintracks/index.d.ts.map +1 -1
  21. package/mobile/out/src/services/chaintracker/chaintracks/index.js +1 -1
  22. package/mobile/out/src/services/chaintracker/chaintracks/index.js.map +1 -1
  23. package/mobile/out/src/signer/methods/internalizeAction.d.ts +2 -2
  24. package/mobile/out/src/signer/methods/internalizeAction.d.ts.map +1 -1
  25. package/mobile/out/src/signer/methods/internalizeAction.js.map +1 -1
  26. package/mobile/out/src/storage/StorageProvider.d.ts +5 -6
  27. package/mobile/out/src/storage/StorageProvider.d.ts.map +1 -1
  28. package/mobile/out/src/storage/StorageProvider.js +75 -101
  29. package/mobile/out/src/storage/StorageProvider.js.map +1 -1
  30. package/mobile/out/src/storage/WalletStorageManager.d.ts +2 -2
  31. package/mobile/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  32. package/mobile/out/src/storage/methods/generateChange.d.ts +0 -24
  33. package/mobile/out/src/storage/methods/generateChange.d.ts.map +1 -1
  34. package/mobile/out/src/storage/methods/generateChange.js +2 -50
  35. package/mobile/out/src/storage/methods/generateChange.js.map +1 -1
  36. package/mobile/out/src/storage/methods/getBeefForTransaction.js +3 -2
  37. package/mobile/out/src/storage/methods/getBeefForTransaction.js.map +1 -1
  38. package/mobile/out/src/storage/methods/internalizeAction.d.ts +2 -10
  39. package/mobile/out/src/storage/methods/internalizeAction.d.ts.map +1 -1
  40. package/mobile/out/src/storage/methods/internalizeAction.js +16 -1
  41. package/mobile/out/src/storage/methods/internalizeAction.js.map +1 -1
  42. package/mobile/out/src/storage/methods/processAction.d.ts +16 -1
  43. package/mobile/out/src/storage/methods/processAction.d.ts.map +1 -1
  44. package/mobile/out/src/storage/methods/processAction.js +4 -1
  45. package/mobile/out/src/storage/methods/processAction.js.map +1 -1
  46. package/mobile/out/src/storage/methods/utils.d.ts +25 -0
  47. package/mobile/out/src/storage/methods/utils.d.ts.map +1 -0
  48. package/mobile/out/src/storage/methods/utils.js +53 -0
  49. package/mobile/out/src/storage/methods/utils.js.map +1 -0
  50. package/mobile/out/src/storage/remoting/StorageClient.d.ts +2 -2
  51. package/mobile/out/src/storage/remoting/StorageClient.d.ts.map +1 -1
  52. package/mobile/out/src/storage/remoting/StorageClient.js.map +1 -1
  53. package/mobile/out/src/storage/remoting/StorageMobile.d.ts +2 -2
  54. package/mobile/out/src/storage/remoting/StorageMobile.d.ts.map +1 -1
  55. package/mobile/out/src/storage/remoting/StorageMobile.js.map +1 -1
  56. package/mobile/out/src/utility/utilityHelpers.d.ts +1 -1
  57. package/mobile/out/src/utility/utilityHelpers.d.ts.map +1 -1
  58. package/mobile/out/src/utility/utilityHelpers.js +3 -3
  59. package/mobile/out/src/utility/utilityHelpers.js.map +1 -1
  60. package/mobile/package-lock.json +2 -2
  61. package/mobile/package.json +1 -1
  62. package/out/src/Wallet.d.ts +1 -1
  63. package/out/src/Wallet.d.ts.map +1 -1
  64. package/out/src/Wallet.js +16 -6
  65. package/out/src/Wallet.js.map +1 -1
  66. package/out/src/monitor/Monitor.d.ts.map +1 -1
  67. package/out/src/monitor/Monitor.js +2 -4
  68. package/out/src/monitor/Monitor.js.map +1 -1
  69. package/out/src/monitor/tasks/TaskNewHeader.d.ts +1 -1
  70. package/out/src/monitor/tasks/TaskNewHeader.d.ts.map +1 -1
  71. package/out/src/sdk/WalletStorage.interfaces.d.ts +14 -1
  72. package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
  73. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.d.ts.map +1 -0
  74. package/out/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.js.map +1 -0
  75. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts +1 -1
  76. package/out/src/services/chaintracker/chaintracks/ChaintracksServiceClient.d.ts.map +1 -1
  77. package/out/src/services/chaintracker/chaintracks/index.d.ts +1 -1
  78. package/out/src/services/chaintracker/chaintracks/index.d.ts.map +1 -1
  79. package/out/src/services/chaintracker/chaintracks/index.js +1 -1
  80. package/out/src/services/chaintracker/chaintracks/index.js.map +1 -1
  81. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.d.ts +144 -0
  82. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.d.ts.map +1 -0
  83. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.js +463 -0
  84. package/out/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.js.map +1 -0
  85. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.d.ts +20 -0
  86. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.d.ts.map +1 -0
  87. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.js +31 -0
  88. package/out/src/services/chaintracker/chaintracks/util/dirtyHashes.js.map +1 -0
  89. package/out/src/signer/methods/internalizeAction.d.ts +2 -2
  90. package/out/src/signer/methods/internalizeAction.d.ts.map +1 -1
  91. package/out/src/signer/methods/internalizeAction.js.map +1 -1
  92. package/out/src/storage/StorageProvider.d.ts +5 -6
  93. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  94. package/out/src/storage/StorageProvider.js +75 -101
  95. package/out/src/storage/StorageProvider.js.map +1 -1
  96. package/out/src/storage/WalletStorageManager.d.ts +2 -2
  97. package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
  98. package/out/src/storage/__test/StorageIdb.test.js +1 -0
  99. package/out/src/storage/__test/StorageIdb.test.js.map +1 -1
  100. package/out/src/storage/__test/adminStats.man.test.js +2 -0
  101. package/out/src/storage/__test/adminStats.man.test.js.map +1 -1
  102. package/out/src/storage/methods/generateChange.d.ts +0 -24
  103. package/out/src/storage/methods/generateChange.d.ts.map +1 -1
  104. package/out/src/storage/methods/generateChange.js +2 -50
  105. package/out/src/storage/methods/generateChange.js.map +1 -1
  106. package/out/src/storage/methods/getBeefForTransaction.js +3 -2
  107. package/out/src/storage/methods/getBeefForTransaction.js.map +1 -1
  108. package/out/src/storage/methods/internalizeAction.d.ts +2 -10
  109. package/out/src/storage/methods/internalizeAction.d.ts.map +1 -1
  110. package/out/src/storage/methods/internalizeAction.js +16 -1
  111. package/out/src/storage/methods/internalizeAction.js.map +1 -1
  112. package/out/src/storage/methods/processAction.d.ts +16 -1
  113. package/out/src/storage/methods/processAction.d.ts.map +1 -1
  114. package/out/src/storage/methods/processAction.js +4 -1
  115. package/out/src/storage/methods/processAction.js.map +1 -1
  116. package/out/src/storage/methods/utils.Buffer.d.ts +21 -0
  117. package/out/src/storage/methods/utils.Buffer.d.ts.map +1 -0
  118. package/out/src/storage/methods/utils.Buffer.js +37 -0
  119. package/out/src/storage/methods/utils.Buffer.js.map +1 -0
  120. package/out/src/storage/methods/utils.d.ts +25 -0
  121. package/out/src/storage/methods/utils.d.ts.map +1 -0
  122. package/out/src/storage/methods/utils.js +53 -0
  123. package/out/src/storage/methods/utils.js.map +1 -0
  124. package/out/src/storage/remoting/StorageClient.d.ts +2 -2
  125. package/out/src/storage/remoting/StorageClient.d.ts.map +1 -1
  126. package/out/src/storage/remoting/StorageClient.js.map +1 -1
  127. package/out/src/storage/remoting/StorageMobile.d.ts +2 -2
  128. package/out/src/storage/remoting/StorageMobile.d.ts.map +1 -1
  129. package/out/src/storage/remoting/StorageMobile.js.map +1 -1
  130. package/out/src/utility/Format.d.ts +14 -0
  131. package/out/src/utility/Format.d.ts.map +1 -0
  132. package/out/src/utility/Format.js +167 -0
  133. package/out/src/utility/Format.js.map +1 -0
  134. package/out/src/utility/utilityHelpers.d.ts +1 -1
  135. package/out/src/utility/utilityHelpers.d.ts.map +1 -1
  136. package/out/src/utility/utilityHelpers.js +3 -3
  137. package/out/src/utility/utilityHelpers.js.map +1 -1
  138. package/out/test/Wallet/support/operations.man.test.js +18 -138
  139. package/out/test/Wallet/support/operations.man.test.js.map +1 -1
  140. package/out/test/Wallet/support/reqErrorReview.2025.05.06.man.test.d.ts +2 -0
  141. package/out/test/Wallet/support/reqErrorReview.2025.05.06.man.test.d.ts.map +1 -0
  142. package/out/test/Wallet/support/reqErrorReview.2025.05.06.man.test.js +385 -0
  143. package/out/test/Wallet/support/reqErrorReview.2025.05.06.man.test.js.map +1 -0
  144. package/out/test/utils/TestUtilsWalletStorage.d.ts +5 -0
  145. package/out/test/utils/TestUtilsWalletStorage.d.ts.map +1 -1
  146. package/out/test/utils/TestUtilsWalletStorage.js +20 -0
  147. package/out/test/utils/TestUtilsWalletStorage.js.map +1 -1
  148. package/out/tsconfig.all.tsbuildinfo +1 -1
  149. package/package.json +1 -1
  150. package/src/Wallet.ts +25 -10
  151. package/src/monitor/Monitor.ts +2 -4
  152. package/src/monitor/tasks/TaskNewHeader.ts +1 -1
  153. package/src/sdk/WalletStorage.interfaces.ts +16 -1
  154. package/src/services/chaintracker/chaintracks/ChaintracksServiceClient.ts +1 -1
  155. package/src/services/chaintracker/chaintracks/index.ts +1 -1
  156. package/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.ts +490 -0
  157. package/src/services/chaintracker/chaintracks/util/dirtyHashes.ts +29 -0
  158. package/src/signer/methods/internalizeAction.ts +2 -2
  159. package/src/storage/StorageProvider.ts +43 -31
  160. package/src/storage/WalletStorageManager.ts +1 -1
  161. package/src/storage/__test/StorageIdb.test.ts +1 -0
  162. package/src/storage/__test/adminStats.man.test.ts +2 -0
  163. package/src/storage/methods/generateChange.ts +1 -54
  164. package/src/storage/methods/getBeefForTransaction.ts +10 -2
  165. package/src/storage/methods/internalizeAction.ts +22 -14
  166. package/src/storage/methods/processAction.ts +5 -2
  167. package/src/storage/methods/utils.Buffer.ts +33 -0
  168. package/src/storage/methods/utils.ts +56 -0
  169. package/src/storage/remoting/StorageClient.ts +2 -2
  170. package/src/storage/remoting/StorageMobile.ts +2 -2
  171. package/src/utility/Format.ts +133 -0
  172. package/src/utility/utilityHelpers.ts +2 -2
  173. package/test/Wallet/support/operations.man.test.ts +20 -125
  174. package/test/Wallet/support/reqErrorReview.2025.05.06.man.test.ts +359 -0
  175. package/test/utils/TestUtilsWalletStorage.ts +23 -0
  176. package/mobile/out/src/services/chaintracker/chaintracks/BlockHeaderApi.d.ts.map +0 -1
  177. package/mobile/out/src/services/chaintracker/chaintracks/BlockHeaderApi.js.map +0 -1
  178. package/out/src/services/chaintracker/chaintracks/BlockHeaderApi.d.ts.map +0 -1
  179. package/out/src/services/chaintracker/chaintracks/BlockHeaderApi.js.map +0 -1
  180. /package/mobile/out/src/services/chaintracker/chaintracks/{BlockHeaderApi.d.ts → Api/BlockHeaderApi.d.ts} +0 -0
  181. /package/mobile/out/src/services/chaintracker/chaintracks/{BlockHeaderApi.js → Api/BlockHeaderApi.js} +0 -0
  182. /package/out/src/services/chaintracker/chaintracks/{BlockHeaderApi.d.ts → Api/BlockHeaderApi.d.ts} +0 -0
  183. /package/out/src/services/chaintracker/chaintracks/{BlockHeaderApi.js → Api/BlockHeaderApi.js} +0 -0
  184. /package/src/services/chaintracker/chaintracks/{BlockHeaderApi.ts → Api/BlockHeaderApi.ts} +0 -0
@@ -1,36 +1,15 @@
1
- import { Transaction, WalletOutput } from '@bsv/sdk'
2
- import {
3
- sdk,
4
- Services,
5
- Setup,
6
- StorageKnex,
7
- TableOutput,
8
- TableTransaction,
9
- TableUser,
10
- verifyOne,
11
- verifyOneOrNone
12
- } from '../../../src'
13
- import { _tu, TuEnv } from '../../utils/TestUtilsWalletStorage'
1
+ import { WalletOutput } from '@bsv/sdk'
2
+ import { sdk, TableOutput, TableUser, verifyOne, verifyOneOrNone } from '../../../src'
3
+ import { _tu } from '../../utils/TestUtilsWalletStorage'
14
4
  import { specOpInvalidChange, ValidListOutputsArgs } from '../../../src/sdk'
15
5
  import { LocalWalletTestOptions } from '../../utils/localWalletMethods'
16
-
17
- import * as dotenv from 'dotenv'
18
- dotenv.config()
19
-
20
- const chain: sdk.Chain = 'main'
21
-
22
- const options: LocalWalletTestOptions = {
23
- setActiveClient: true,
24
- useMySQLConnectionForClient: true,
25
- useTestIdentityKey: false,
26
- useIdentityKey2: false
27
- }
6
+ import { Format } from '../../../src/utility/Format'
28
7
 
29
8
  describe('operations.man tests', () => {
30
9
  jest.setTimeout(99999999)
31
10
 
32
11
  test('0 review and release all production invalid change utxos', async () => {
33
- const { env, storage } = await createMainReviewSetup()
12
+ const { env, storage } = await _tu.createMainReviewSetup()
34
13
  const users = await storage.findUsers({ partial: {} })
35
14
  const withInvalid: Record<number, { user: TableUser; outputs: WalletOutput[]; total: number }> = {}
36
15
  const vargs: ValidListOutputsArgs = {
@@ -67,45 +46,9 @@ describe('operations.man tests', () => {
67
46
  await storage.destroy()
68
47
  })
69
48
 
70
- test.skip('0a review all spendable outputs for userId', async () => {
71
- const { env, storage } = await createMainReviewSetup()
72
- const users = await storage.findUsers({ partial: {} })
73
- const withInvalid: Record<number, { user: TableUser; outputs: WalletOutput[]; total: number }> = {}
74
- const vargs: ValidListOutputsArgs = {
75
- basket: specOpInvalidChange,
76
- tags: ['release', 'all'],
77
- tagQueryMode: 'all',
78
- includeLockingScripts: false,
79
- includeTransactions: false,
80
- includeCustomInstructions: false,
81
- includeTags: false,
82
- includeLabels: false,
83
- limit: 0,
84
- offset: 0,
85
- seekPermission: false,
86
- knownTxids: []
87
- }
88
- let log = ''
89
- for (const user of users) {
90
- const { userId } = user
91
- const auth = { userId, identityKey: '' }
92
- let r = await storage.listOutputs(auth, vargs)
93
- if (r.totalOutputs > 0) {
94
- const total: number = r.outputs.reduce((s, o) => (s += o.satoshis), 0)
95
- log += `userId ${userId}: ${r.totalOutputs} unspendable utxos, total ${total}, ${user.identityKey}\n`
96
- for (const o of r.outputs) {
97
- log += ` ${o.outpoint} ${o.satoshis}\n`
98
- }
99
- withInvalid[userId] = { user, outputs: r.outputs, total }
100
- }
101
- }
102
- console.log(log || 'Found zero invalid change outputs.')
103
- await storage.destroy()
104
- })
105
-
106
49
  test('1 review and unfail false doubleSpends', async () => {
107
- const { env, storage, services } = await createMainReviewSetup()
108
- let offset = 2400
50
+ const { env, storage, services } = await _tu.createMainReviewSetup()
51
+ let offset = 2700
109
52
  const limit = 100
110
53
  let allUnfails: number[] = []
111
54
  for (;;) {
@@ -131,8 +74,8 @@ describe('operations.man tests', () => {
131
74
  })
132
75
 
133
76
  test('2 review and unfail false invalids', async () => {
134
- const { env, storage, services } = await createMainReviewSetup()
135
- let offset = 700
77
+ const { env, storage, services } = await _tu.createMainReviewSetup()
78
+ let offset = 800
136
79
  const limit = 100
137
80
  let allUnfails: number[] = []
138
81
  for (;;) {
@@ -158,9 +101,11 @@ describe('operations.man tests', () => {
158
101
  await storage.destroy()
159
102
  })
160
103
 
161
- test.skip('8 re-internalize failed WUI exports', async () => {
162
- const { env, storage, services } = await createMainReviewSetup()
104
+ test.skip('10 re-internalize failed WUI exports', async () => {
105
+ const { env, storage, services } = await _tu.createMainReviewSetup()
106
+ // From this user
163
107
  const user0 = verifyOne(await storage.findUsers({ partial: { userId: 2 } }))
108
+ // To these users
164
109
  const users = await storage.findUsers({ partial: { userId: 141 } }) // 111, 141
165
110
  for (const user of users) {
166
111
  const { userId, identityKey } = user
@@ -204,19 +149,21 @@ describe('operations.man tests', () => {
204
149
  */
205
150
  await storage.destroy()
206
151
  })
207
- test('9 review recent transaction change use', async () => {
208
- const { env, storage, services } = await createMainReviewSetup()
152
+
153
+ test.skip('11 review recent transaction change use for specific userId', async () => {
154
+ const userId = 311
155
+ const { env, storage, services } = await _tu.createMainReviewSetup()
209
156
  const countTxs = await storage.countTransactions({
210
- partial: { userId: 311 },
157
+ partial: { userId },
211
158
  status: ['completed', 'unproven', 'failed']
212
159
  })
213
160
  const txs = await storage.findTransactions({
214
- partial: { userId: 311 },
161
+ partial: { userId },
215
162
  status: ['unproven', 'completed', 'failed'],
216
163
  paged: { limit: 100, offset: Math.max(0, countTxs - 100) }
217
164
  })
218
165
  for (const tx of txs) {
219
- const ls = await toLogString(tx, storage)
166
+ const ls = await Format.toLogStringTableTransaction(tx, storage)
220
167
  console.log(ls)
221
168
  }
222
169
  const countReqs = await storage.countProvenTxReqs({ partial: {}, status: ['completed', 'unmined'] })
@@ -228,55 +175,3 @@ describe('operations.man tests', () => {
228
175
  await storage.destroy()
229
176
  })
230
177
  })
231
-
232
- async function createMainReviewSetup(): Promise<{
233
- env: TuEnv
234
- storage: StorageKnex
235
- services: Services
236
- }> {
237
- const env = _tu.getEnv('main')
238
- const knex = Setup.createMySQLKnex(process.env.MAIN_CLOUD_MYSQL_CONNECTION!)
239
- const storage = new StorageKnex({
240
- chain: env.chain,
241
- knex: knex,
242
- commissionSatoshis: 0,
243
- commissionPubKeyHex: undefined,
244
- feeModel: { model: 'sat/kb', value: 1 }
245
- })
246
- const servicesOptions = Services.createDefaultOptions(env.chain)
247
- if (env.whatsonchainApiKey) servicesOptions.whatsOnChainApiKey = env.whatsonchainApiKey
248
- const services = new Services(servicesOptions)
249
- storage.setServices(services)
250
- await storage.makeAvailable()
251
- return { env, storage, services }
252
- }
253
-
254
- async function toLogString(tx: TableTransaction, storage: StorageKnex): Promise<string> {
255
- const rawTx = await storage.getRawTxOfKnownValidTransaction(tx.txid)
256
- const btx = Transaction.fromBinary(rawTx!)
257
- let log = `tx ${tx.txid} ${tx.status} s:${tx.satoshis} uid:${tx.userId} tid:${tx.transactionId}\n`
258
- for (let i = 0; i < Math.max(btx.inputs.length, btx.outputs.length); i++) {
259
- let ilog: string = ''
260
- let olog: string = ''
261
- if (i < btx.inputs.length) {
262
- const input = btx.inputs[i]
263
- ilog = `${logTxid(input.sourceTXID!)}.${input.sourceOutputIndex}`
264
- }
265
- if (i < btx.outputs.length) {
266
- const output = btx.outputs[i]
267
- olog = `${ar('' + output.satoshis, 9)} ${output.lockingScript.toHex().length / 2}`
268
- }
269
- log += `${ar('' + i, 5)} ${al(ilog, 20)} ${olog}\n`
270
- }
271
- return log
272
-
273
- function logTxid(txid: string): string {
274
- return `${txid.slice(0, 6)}..${txid.slice(-6)}`
275
- }
276
- function al(v: string | number, w: number): string {
277
- return v.toString().padEnd(w)
278
- }
279
- function ar(v: string | number, w: number): string {
280
- return v.toString().padStart(w)
281
- }
282
- }
@@ -0,0 +1,359 @@
1
+ import { Beef, Transaction, WalletOutput } from '@bsv/sdk'
2
+ import {
3
+ EntityProvenTxReq,
4
+ sdk,
5
+ Services,
6
+ Setup,
7
+ StorageKnex,
8
+ TableOutput,
9
+ TableProvenTxReq,
10
+ TableUser,
11
+ verifyOne,
12
+ verifyOneOrNone
13
+ } from '../../../src'
14
+ import { _tu, TuEnv } from '../../utils/TestUtilsWalletStorage'
15
+ import { specOpInvalidChange, ValidListOutputsArgs } from '../../../src/sdk'
16
+ import { LocalWalletTestOptions } from '../../utils/localWalletMethods'
17
+ import { Format } from '../../../src/utility/Format'
18
+
19
+ import * as dotenv from 'dotenv'
20
+ dotenv.config()
21
+
22
+ const chain: sdk.Chain = 'main'
23
+
24
+ const options: LocalWalletTestOptions = {
25
+ setActiveClient: true,
26
+ useMySQLConnectionForClient: true,
27
+ useTestIdentityKey: false,
28
+ useIdentityKey2: false
29
+ }
30
+
31
+ describe('reqErrorReview.2025.05.06.man tests', () => {
32
+ jest.setTimeout(99999999)
33
+
34
+ // OVERWRITES EXISTING FILE CONTENTS!!!!
35
+ test.skip('0 grab reqs history as local sqlite file', async () => {
36
+ const { env, storage, services } = await _tu.createMainReviewSetup()
37
+ const { activeStorage: s2 } = await _tu.createSQLiteTestWallet({
38
+ filePath: `${__dirname}/reqhistory.sqlite`,
39
+ databaseName: 'reqhistory',
40
+ chain: 'main',
41
+ rootKeyHex: '1'.repeat(64),
42
+ dropAll: true
43
+ })
44
+ await s2.makeAvailable()
45
+ const limit = 100
46
+ let offset = 0
47
+ for (;;) {
48
+ const r = await storage.knex.raw(`
49
+ select provenTxReqId as id, txid, status, history
50
+ from proven_tx_reqs
51
+ where history is not null
52
+ limit ${limit} offset ${offset}
53
+ `)
54
+ const reqs = r[0] as { id: number; txid: string; status: sdk.ProvenTxReqStatus; history: string }[]
55
+ for (const req of reqs) {
56
+ const { id, history, status, txid } = req
57
+ await s2.insertProvenTxReq({
58
+ created_at: new Date(),
59
+ updated_at: new Date(),
60
+ provenTxReqId: id,
61
+ status,
62
+ attempts: 0,
63
+ notified: false,
64
+ txid,
65
+ history,
66
+ notify: '',
67
+ rawTx: []
68
+ })
69
+ }
70
+ if (reqs.length < limit) break
71
+ offset += limit
72
+ }
73
+ await s2.destroy()
74
+ await storage.destroy()
75
+ })
76
+
77
+ test('1 review reqs history and final outcome', async () => {
78
+ let undouble: number[] = []
79
+ let uninvalid: number[] = []
80
+ let uncompleted: number[] = []
81
+ let deunmined: number[] = []
82
+ let noSuccessCompleted: number[] = []
83
+ let successDouble: number[] = []
84
+ let internalizeDouble: number[] = []
85
+ let successInvalid: number[] = []
86
+
87
+ const { activeStorage: storage } = await _tu.createSQLiteTestWallet({
88
+ filePath: `${__dirname}/reqhistory.sqlite`,
89
+ databaseName: 'reqhistory',
90
+ chain: 'main',
91
+ rootKeyHex: '1'.repeat(64),
92
+ dropAll: false
93
+ })
94
+ //const { env, storage, services } = await _tu.createMainReviewSetup()
95
+ let limit = 100
96
+ let offset = 0
97
+ let aggSum = -1
98
+ const partial: Partial<TableProvenTxReq> = {}
99
+ let log = ''
100
+ for (;;) {
101
+ const reqs = await storage.findProvenTxReqs({ partial, status: undefined, paged: { limit, offset } })
102
+ for (const reqApi of reqs) {
103
+ if (reqApi.provenTxReqId < 11312) continue
104
+ const r = reviewHistoryNotes(reqApi)
105
+ if (!r) continue
106
+ if (r.isCompleted && r.wasDoubleSpend) {
107
+ undouble.push(reqApi.provenTxReqId)
108
+ let review = ''
109
+ if (r.doubleReview) {
110
+ const rr = r.doubleReview
111
+ review = `0:${rr.status0},1:${rr.status1},2:${rr.status2},Txs:${rr.competingTxs}`
112
+ }
113
+ //log += `undouble ${reqApi.provenTxReqId} arc:${r.brArc} woc:${r.brWoC} bit:${r.brBitails} ${review}\n`
114
+ }
115
+ if (r.isCompleted && r.wasInvalid) {
116
+ uninvalid.push(reqApi.provenTxReqId)
117
+ //log += `uninvalid ${reqApi.provenTxReqId} arc:${r.brArc} woc:${r.brWoC} bit:${r.brBitails}\n`
118
+ }
119
+ if ((r.isDoubleSpend || r.isInvalid) && r.wasCompleted) {
120
+ uncompleted.push(reqApi.provenTxReqId)
121
+ }
122
+ if ((r.isDoubleSpend || r.isInvalid) && r.wasUnmined) {
123
+ if (r.wasInternalize) internalizeDouble.push(reqApi.provenTxReqId)
124
+ else {
125
+ deunmined.push(reqApi.provenTxReqId)
126
+ log += `deunmined ${reqApi.provenTxReqId} arc:${r.brArc} woc:${r.brWoC} bit:${r.brBitails}\n`
127
+ }
128
+ }
129
+ if (r.aggregate && r.aggregate.successCount === 0 && r.isCompleted) {
130
+ noSuccessCompleted.push(reqApi.provenTxReqId)
131
+ }
132
+ if (r.aggregate && r.aggregate.successCount > 0 && r.isDoubleSpend) {
133
+ successDouble.push(reqApi.provenTxReqId)
134
+ }
135
+ if (r.aggregate && r.aggregate.successCount > 0 && r.isInvalid) {
136
+ successInvalid.push(reqApi.provenTxReqId)
137
+ }
138
+ if (r.aggregate && r.aggSum !== aggSum) {
139
+ log += `aggSum changed ${aggSum} to ${r.aggSum} reqId=${reqApi.provenTxReqId}\n`
140
+ aggSum = r.aggSum
141
+ }
142
+ }
143
+ if (reqs.length < limit) break
144
+ offset += limit
145
+ }
146
+ if (undouble.length > 0) log += `undouble: ${JSON.stringify(undouble)}\n`
147
+ if (uninvalid.length > 0) log += `uninvalid: ${JSON.stringify(uninvalid)}\n`
148
+ if (uncompleted.length > 0) log += `uncompleted: ${JSON.stringify(uncompleted)}\n`
149
+ if (deunmined.length > 0) log += `deunmined: ${JSON.stringify(deunmined)}\n`
150
+ if (internalizeDouble.length > 0) log += `internalizeDouble: ${JSON.stringify(internalizeDouble)}\n`
151
+ if (noSuccessCompleted.length > 0) log += `noSuccessCompleted: ${JSON.stringify(noSuccessCompleted)}\n`
152
+ if (successDouble.length > 0) log += `successDouble: ${JSON.stringify(successDouble)}\n`
153
+ if (successInvalid.length > 0) log += `successInvalid: ${JSON.stringify(successInvalid)}\n`
154
+ console.log(log)
155
+ await storage.destroy()
156
+ })
157
+
158
+ const uninvalid = [
159
+ 10822, 12228, 14884, 14948, 1654, 1649, 2654, 2655, 2656, 2658, 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666,
160
+ 2667, 2669, 2707, 2719, 2723, 2724, 2726
161
+ ]
162
+
163
+ const undouble = [
164
+ 10732, 12303, 12476, 14084, 14111, 14956, 14972, 14874, 14789, 14810, 14813, 14817, 14588, 14640, 14641, 14531,
165
+ 2753, 2653, 2657, 2670, 2671, 2681, 2684, 2691, 2732, 4343, 4222, 4124, 4148, 3873, 3735, 3514, 3537, 5074, 5125,
166
+ 4958, 4977, 4730, 4365
167
+ ]
168
+
169
+ const deunmined = [
170
+ 12304, 12305, 12306, 12307, 12480, 12483, 12484, 12488, 12489, 12490, 12497, 14085, 14086, 14087, 14814, 14816,
171
+ 14821, 14953, 15170
172
+ ]
173
+
174
+ test('2 review deunmined reqs', async () => {
175
+ const { env, storage, services } = await _tu.createMainReviewSetup()
176
+
177
+ const chaintracker = await services.getChainTracker()
178
+
179
+ let log = ''
180
+ for (const id of deunmined) {
181
+ const reqApi = await storage.findProvenTxReqById(id)
182
+ if (!reqApi) continue
183
+ const beef = new Beef()
184
+ beef.mergeRawTx(reqApi.rawTx!)
185
+ if (reqApi.inputBEEF) beef.mergeBeef(reqApi.inputBEEF)
186
+ let tx = beef.findTxid(reqApi.txid)!.tx!
187
+ let allInputsFound = true
188
+ let ilog = ''
189
+ for (const input of tx.inputs) {
190
+ if (beef.findTxid(input.sourceTXID!)) continue
191
+ try {
192
+ const ib = await storage.getBeefForTransaction(input.sourceTXID!, {})
193
+ if (ib) beef.mergeBeef(ib)
194
+ } catch (e) {
195
+ if (input.sourceTXID) {
196
+ const r2 = verifyOneOrNone(await storage.findProvenTxReqs({ partial: { txid: input.sourceTXID } }))
197
+ if (r2 && r2.rawTx) {
198
+ const itx = Transaction.fromBinary(r2.rawTx)
199
+ ilog += 'missing input ' + Format.toLogStringTransaction(itx)
200
+ }
201
+ }
202
+ allInputsFound = false
203
+ }
204
+ }
205
+ if (allInputsFound) {
206
+ tx = beef.findAtomicTransaction(reqApi.txid)!
207
+ try {
208
+ const ok = await tx.verify('scripts only')
209
+ log += `${reqApi.provenTxReqId} ${reqApi.txid} ${ok ? 'OK' : 'FAIL'}\n`
210
+ } catch (e: unknown) {
211
+ log += `${reqApi.provenTxReqId} ${reqApi.txid} ${sdk.WalletError.fromUnknown(e).message}\n`
212
+ }
213
+ } else {
214
+ log += `${reqApi.provenTxReqId} FAILED `
215
+ log += Format.toLogStringBeefTxid(beef, reqApi.txid)
216
+ log += ilog
217
+ }
218
+ }
219
+ console.log(log)
220
+
221
+ await storage.destroy()
222
+ })
223
+ })
224
+
225
+ function reviewHistoryNotes(reqApi: TableProvenTxReq): HistoryReviewInfo | undefined {
226
+ const r: HistoryReviewInfo = {
227
+ req: new EntityProvenTxReq(reqApi),
228
+ wasDoubleSpend: false,
229
+ wasInvalid: false,
230
+ wasCompleted: false,
231
+ wasUnmined: false,
232
+ wasInternalize: false,
233
+ isDoubleSpend: false,
234
+ isInvalid: false,
235
+ isCompleted: false,
236
+ aggSum: 0,
237
+ aggregate: undefined
238
+ }
239
+ if (!r.req.history?.notes) return undefined
240
+ for (const note of r.req.history.notes) {
241
+ if (note.what === 'status') {
242
+ const statusWas = note.status_was as sdk.ProvenTxReqStatus
243
+ const statusNow = note.status_now as sdk.ProvenTxReqStatus
244
+ if (statusNow === 'doubleSpend') {
245
+ r.isDoubleSpend = r.wasDoubleSpend = true
246
+ r.isInvalid = false
247
+ r.isCompleted = false
248
+ } else if (statusNow === 'invalid') {
249
+ r.isDoubleSpend = false
250
+ r.isInvalid = r.wasInvalid = true
251
+ r.isCompleted = false
252
+ } else if (statusNow === 'completed') {
253
+ r.isDoubleSpend = false
254
+ r.isInvalid = false
255
+ r.isCompleted = r.wasCompleted = true
256
+ } else if (statusNow === 'unmined') {
257
+ r.isDoubleSpend = false
258
+ r.isInvalid = false
259
+ r.wasUnmined = true
260
+ }
261
+ } else if (note.what === 'aggregateResults') {
262
+ r.aggregate = {
263
+ successCount: note.successCount as number,
264
+ doubleSpendCount: note.doubleSpendCount as number,
265
+ statusErrorCount: note.statusErrorCount as number,
266
+ serviceErrorCount: note.serviceErrorCount as number,
267
+ newReqStatus: note.newReqStatus as sdk.ProvenTxReqStatus
268
+ }
269
+ const a = r.aggregate
270
+ r.aggSum = a.doubleSpendCount + a.statusErrorCount + a.serviceErrorCount + a.successCount
271
+ } else if (note.what === 'confirmDoubleSpend') {
272
+ r.doubleReview = {
273
+ status0: note.getStatus0 as string,
274
+ status1: note.getStatus1 as string,
275
+ status2: note.getStatus2 as string,
276
+ competingTxs: note.competingTxs as string
277
+ }
278
+ } else if (note.what === 'internalizeAction') {
279
+ r.wasInternalize = true
280
+ }
281
+
282
+ if (note.name === 'WoCpostRawTx') {
283
+ if (note.what === 'postRawTxErrorMissingInputs') {
284
+ r.brWoC = 'missingInputs'
285
+ } else if (note.what === 'postRawTxError') {
286
+ if (note.status === 504) {
287
+ r.brWoC = 'serviceError'
288
+ }
289
+ }
290
+ } else if (note.name === 'WoCpostBeef') {
291
+ if (note.what === 'postBeefSuccess') {
292
+ r.brWoC = 'success'
293
+ } else if (note.what === 'postBeefError' && r.brWoC === undefined) {
294
+ r.brWoC = 'invalidTx'
295
+ }
296
+ } else if (note.name === 'ARCpostBeef') {
297
+ if (note.what === 'postBeefGetTxDataSuccess') {
298
+ if (note.txStatus === 'STORED') r.brArc = 'success'
299
+ }
300
+ } else if (note.name === 'ARCv1tx') {
301
+ if (note.what === 'postRawTxDoubleSpend') {
302
+ if (note.txStatus === 'DOUBLE_SPEND_ATTEMPTED') r.brArc = 'doubleSpend'
303
+ } else if (note.what === 'postRawTxError') {
304
+ if (note.status === 469) r.brArc = 'badRoots'
305
+ else if (note.status === 463) r.brArc = 'badBump'
306
+ } else if (note.what === 'postRawTxSuccess') {
307
+ if (note.txStatus === 'ANNOUNCED_TO_NETWORK') r.brArc = 'success'
308
+ else if (note.txStatus === 'SEEN_ON_NETWORK') r.brArc = 'success'
309
+ else if (note.txStatus === 'REQUESTED_BY_NETWORK') r.brArc = 'success'
310
+ }
311
+ } else if (note.name === 'BitailsPostRawTx') {
312
+ if (note.what === 'postRawsSuccess') {
313
+ r.brBitails = 'success'
314
+ } else if (note.what === 'postRawsSuccessAlreadyInMempool') {
315
+ r.brBitails = 'success'
316
+ } else if (note.what === 'postRawsErrorMissingInputs') {
317
+ r.brBitails = 'invalidTx'
318
+ } else if (note.what === 'postRawsError') {
319
+ if (note.code === -26) {
320
+ r.brBitails = 'invalidTx'
321
+ } else if (note.code === -1) {
322
+ r.brBitails = 'serviceError'
323
+ } else if (note.code === 'ESOCKETTIMEDOUT') {
324
+ r.brBitails = 'serviceError'
325
+ }
326
+ }
327
+ }
328
+ }
329
+ return r
330
+ }
331
+
332
+ interface HistoryReviewInfo {
333
+ brArc?: string
334
+ brWoC?: string
335
+ brBitails?: string
336
+ req: EntityProvenTxReq
337
+ wasDoubleSpend: boolean
338
+ wasInvalid: boolean
339
+ wasCompleted: boolean
340
+ wasUnmined: boolean
341
+ wasInternalize: boolean
342
+ isDoubleSpend: boolean
343
+ isInvalid: boolean
344
+ isCompleted: boolean
345
+ aggSum: number
346
+ aggregate?: {
347
+ successCount: number
348
+ doubleSpendCount: number
349
+ statusErrorCount: number
350
+ serviceErrorCount: number
351
+ newReqStatus: sdk.ProvenTxReqStatus
352
+ }
353
+ doubleReview?: {
354
+ status0: string
355
+ status1: string
356
+ status2: string
357
+ competingTxs: string
358
+ }
359
+ }
@@ -163,6 +163,29 @@ export abstract class TestUtilsWalletStorage {
163
163
  }
164
164
  }
165
165
 
166
+ static async createMainReviewSetup(): Promise<{
167
+ env: TuEnv
168
+ storage: StorageKnex
169
+ services: Services
170
+ }> {
171
+ const env = _tu.getEnv('main')
172
+ if (!env.cloudMySQLConnection) throw new sdk.WERR_INVALID_PARAMETER('env.cloundMySQLConnection', 'valid')
173
+ const knex = Setup.createMySQLKnex(env.cloudMySQLConnection)
174
+ const storage = new StorageKnex({
175
+ chain: env.chain,
176
+ knex: knex,
177
+ commissionSatoshis: 0,
178
+ commissionPubKeyHex: undefined,
179
+ feeModel: { model: 'sat/kb', value: 1 }
180
+ })
181
+ const servicesOptions = Services.createDefaultOptions(env.chain)
182
+ if (env.whatsonchainApiKey) servicesOptions.whatsOnChainApiKey = env.whatsonchainApiKey
183
+ const services = new Services(servicesOptions)
184
+ storage.setServices(services)
185
+ await storage.makeAvailable()
186
+ return { env, storage, services }
187
+ }
188
+
166
189
  static async createNoSendP2PKHTestOutpoint(
167
190
  address: string,
168
191
  satoshis: number,
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockHeaderApi.d.ts","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/BlockHeaderApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAMD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEvF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEpH;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEhH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEpH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockHeaderApi.js","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/BlockHeaderApi.ts"],"names":[],"mappings":";;AAwFA,wBAEC;AAMD,8CAEC;AAMD,sCAEC;AAMD,8CAEC;AAlCD,EAAE;AACF,cAAc;AACd,EAAE;AAEF;;;GAGG;AACH,SAAgB,MAAM,CAAC,MAAqC;IAC1D,OAAQ,MAA0B,CAAC,QAAQ,KAAK,SAAS,CAAA;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAuD;IACvF,OAAO,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AAChD,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,MAAuD;IACnF,OAAO,QAAQ,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AACtE,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAuD;IACvF,OAAO,WAAW,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AACzE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockHeaderApi.d.ts","sourceRoot":"","sources":["../../../../../src/services/chaintracker/chaintracks/BlockHeaderApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAMD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEvF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEpH;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEhH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,IAAI,eAAe,CAEpH"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockHeaderApi.js","sourceRoot":"","sources":["../../../../../src/services/chaintracker/chaintracks/BlockHeaderApi.ts"],"names":[],"mappings":";;AAwFA,wBAEC;AAMD,8CAEC;AAMD,sCAEC;AAMD,8CAEC;AAlCD,EAAE;AACF,cAAc;AACd,EAAE;AAEF;;;GAGG;AACH,SAAgB,MAAM,CAAC,MAAqC;IAC1D,OAAQ,MAA0B,CAAC,QAAQ,KAAK,SAAS,CAAA;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAuD;IACvF,OAAO,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AAChD,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,MAAuD;IACnF,OAAO,QAAQ,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AACtE,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,MAAuD;IACvF,OAAO,WAAW,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,CAAA;AACzE,CAAC"}