@bsv/wallet-toolbox 1.1.51 → 1.1.53

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.1.51",
3
+ "version": "1.1.53",
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",
@@ -7,6 +7,89 @@ import {
7
7
  import { TableOutput, TableOutputBasket, TableOutputTag } from '../index.client'
8
8
  import { asString, sdk, verifyId, verifyOne } from '../../index.client'
9
9
  import { StorageKnex } from '../StorageKnex'
10
+ import { ValidListOutputsArgs } from '../../sdk'
11
+
12
+ interface SpecOp {
13
+ name: string
14
+ useBasket?: string
15
+ ignoreLimit?: boolean
16
+ resultFromOutputs?: (
17
+ s: StorageKnex,
18
+ vargs: ValidListOutputsArgs,
19
+ outputs: TableOutput[]
20
+ ) => Promise<ListOutputsResult>
21
+ filterOutputs?: (
22
+ s: StorageKnex,
23
+ vargs: ValidListOutputsArgs,
24
+ outputs: TableOutput[]
25
+ ) => Promise<TableOutput[]>
26
+ }
27
+
28
+ const basketToSpecOp: Record<string, SpecOp> = {
29
+ f61dc9289ceea604247ebde125b93a4099931f69c0e95ecd43ce0a480b9bb6c9: {
30
+ name: 'reserved...'
31
+ },
32
+ '69a57cc2b34058d5218927ecfd3e9e4254d8395b6fda9d57c689c753c6d6cad5': {
33
+ name: 'reserved...'
34
+ },
35
+ '893b7646de0e1c9f741bd6e9169b76a8847ae34adef7bef1e6a285371206d2e8': {
36
+ name: 'totalOutputsIsWalletBalance',
37
+ useBasket: 'default',
38
+ ignoreLimit: true,
39
+ resultFromOutputs: async (
40
+ s: StorageKnex,
41
+ vargs: ValidListOutputsArgs,
42
+ outputs: TableOutput[]
43
+ ): Promise<ListOutputsResult> => {
44
+ let totalOutputs = 0
45
+ for (const o of outputs) totalOutputs += o.satoshis
46
+ return { totalOutputs, outputs: [] }
47
+ }
48
+ },
49
+ '5a76fd430a311f8bc0553859061710a4475c19fed46e2ff95969aa918e612e57': {
50
+ name: 'invalidChangeOutputs',
51
+ useBasket: 'default',
52
+ ignoreLimit: true,
53
+ filterOutputs: async (
54
+ s: StorageKnex,
55
+ vargs: ValidListOutputsArgs,
56
+ outputs: TableOutput[]
57
+ ): Promise<TableOutput[]> => {
58
+ const filteredOutputs: TableOutput[] = []
59
+ let ok = false
60
+
61
+ for (const o of outputs) {
62
+ if (o.lockingScript && o.lockingScript.length > 0) {
63
+ const r = await s
64
+ .getServices()
65
+ .getUtxoStatus(asString(o.lockingScript), 'script')
66
+ if (r.status === 'success' && r.isUtxo && r.details?.length > 0) {
67
+ if (
68
+ r.details.some(
69
+ d =>
70
+ d.txid === o.txid &&
71
+ d.satoshis === o.satoshis &&
72
+ d.index === o.vout
73
+ )
74
+ ) {
75
+ ok = true
76
+ }
77
+ }
78
+ }
79
+
80
+ if (!ok) {
81
+ filteredOutputs.push(o)
82
+ }
83
+ }
84
+ if (vargs.tags[0] === 'release') {
85
+ for (const o of filteredOutputs) {
86
+ await s.updateOutput(o.outputId, { spendable: false })
87
+ }
88
+ }
89
+ return filteredOutputs
90
+ }
91
+ }
92
+ }
10
93
 
11
94
  export async function listOutputs(
12
95
  dsk: StorageKnex,
@@ -38,20 +121,26 @@ export async function listOutputs(
38
121
  }
39
122
  */
40
123
 
124
+ let specOp: SpecOp | undefined = undefined
41
125
  let basketId: number | undefined = undefined
42
126
  const basketsById: Record<number, TableOutputBasket> = {}
43
127
  if (vargs.basket) {
44
- const baskets = await dsk.findOutputBaskets({
45
- partial: { userId, name: vargs.basket },
46
- trx
47
- })
48
- if (baskets.length !== 1) {
49
- // If basket does not exist, result is no outputs.
50
- return r
128
+ let b = vargs.basket
129
+ specOp = basketToSpecOp[b]
130
+ b = specOp ? (specOp.useBasket ? specOp.useBasket : '') : b
131
+ if (b) {
132
+ const baskets = await dsk.findOutputBaskets({
133
+ partial: { userId, name: b },
134
+ trx
135
+ })
136
+ if (baskets.length !== 1) {
137
+ // If basket does not exist, result is no outputs.
138
+ return r
139
+ }
140
+ const basket = baskets[0]
141
+ basketId = basket.basketId!
142
+ basketsById[basketId!] = basket
51
143
  }
52
- const basket = baskets[0]
53
- basketId = basket.basketId!
54
- basketsById[basketId!] = basket
55
144
  }
56
145
 
57
146
  let tagIds: number[] = []
@@ -132,9 +221,20 @@ export async function listOutputs(
132
221
  : makeWithTagsQueries()
133
222
 
134
223
  // Sort order when limit and offset are possible must be ascending for determinism.
135
- q.limit(limit).offset(offset).orderBy('outputId', 'asc')
224
+ if (!specOp || !specOp.ignoreLimit) q.limit(limit).offset(offset)
136
225
 
137
- const outputs: TableOutput[] = await q
226
+ q.orderBy('outputId', 'asc')
227
+
228
+ let outputs: TableOutput[] = await q
229
+
230
+ if (specOp) {
231
+ if (specOp.filterOutputs)
232
+ outputs = await specOp.filterOutputs(dsk, vargs, outputs)
233
+ if (specOp.resultFromOutputs) {
234
+ const r = await specOp.resultFromOutputs(dsk, vargs, outputs)
235
+ return r
236
+ }
237
+ }
138
238
 
139
239
  if (!limit || outputs.length < limit) r.totalOutputs = outputs.length
140
240
  else {
@@ -242,7 +242,7 @@ export class EntityTransaction extends EntityBase<TableTransaction> {
242
242
  (ei.provenTxId &&
243
243
  eo.provenTxId !==
244
244
  (syncMap
245
- ? syncMap.transaction.idMap[verifyId(ei.provenTxId)]
245
+ ? syncMap.provenTx.idMap[verifyId(ei.provenTxId)]
246
246
  : ei.provenTxId))
247
247
  )
248
248
  return false
@@ -303,7 +303,7 @@ export class EntityTransaction extends EntityBase<TableTransaction> {
303
303
  this.isOutgoing = ei.isOutgoing
304
304
  this.status = ei.status
305
305
  this.provenTxId = ei.provenTxId
306
- ? syncMap.transaction.idMap[ei.provenTxId]
306
+ ? syncMap.provenTx.idMap[ei.provenTxId]
307
307
  : undefined
308
308
  this.satoshis = ei.satoshis
309
309
  this.txid = ei.txid
@@ -163,7 +163,8 @@ async function createSetup(chain: sdk.Chain): Promise<TestWalletNoSetup> {
163
163
  rootKeyHex: env.devKeys[env.testIdentityKey],
164
164
  filePath: env.testFilePath,
165
165
  setActiveClient: false,
166
- addLocalBackup: false
166
+ addLocalBackup: false,
167
+ useMySQLConnectionForClient: false
167
168
  })
168
169
 
169
170
  console.log(`ACTIVE STORAGE: ${setup.storage.getActiveStoreName()}`)
@@ -0,0 +1,58 @@
1
+ import { sdk } from '../../../src'
2
+ import { _tu, TestWalletNoSetup } from '../../utils/TestUtilsWalletStorage'
3
+
4
+ describe('specOps tests', () => {
5
+ jest.setTimeout(99999999)
6
+
7
+ test('00', () => {})
8
+ if (_tu.noTestEnv('test')) return
9
+ if (_tu.noTestEnv('main')) return
10
+
11
+ test('0 wallet balance specOp', async () => {
12
+ const setup = await createSetup('test')
13
+
14
+ const r = await setup.wallet.listOutputs({
15
+ basket: '893b7646de0e1c9f741bd6e9169b76a8847ae34adef7bef1e6a285371206d2e8'
16
+ })
17
+
18
+ expect(r.totalOutputs > 0).toBe(true)
19
+ expect(r.outputs.length === 0).toBe(true)
20
+
21
+ await setup.wallet.destroy()
22
+ })
23
+
24
+ test('1 wallet invalid change outputs', async () => {
25
+ const setup = await createSetup('test')
26
+
27
+ const r = await setup.wallet.listOutputs({
28
+ basket: '5a76fd430a311f8bc0553859061710a4475c19fed46e2ff95969aa918e612e57'
29
+ // tags: ['release']
30
+ })
31
+
32
+ expect(r.totalOutputs).toBe(0)
33
+ expect(r.outputs.length).toBe(0)
34
+
35
+ await setup.wallet.destroy()
36
+ })
37
+ })
38
+
39
+ async function createSetup(chain: sdk.Chain): Promise<TestWalletNoSetup> {
40
+ const env = _tu.getEnv(chain)
41
+ if (!env.testIdentityKey)
42
+ throw new sdk.WERR_INVALID_PARAMETER('env.testIdentityKey', 'valid')
43
+ if (!env.testFilePath)
44
+ throw new sdk.WERR_INVALID_PARAMETER('env.testFilePath', 'valid')
45
+
46
+ const setup = await _tu.createTestWallet({
47
+ chain,
48
+ rootKeyHex: env.devKeys[env.testIdentityKey],
49
+ filePath: env.testFilePath,
50
+ setActiveClient: false,
51
+ addLocalBackup: false,
52
+ useMySQLConnectionForClient: false
53
+ })
54
+
55
+ console.log(`ACTIVE STORAGE: ${setup.storage.getActiveStoreName()}`)
56
+
57
+ return setup
58
+ }
@@ -352,6 +352,7 @@ export abstract class TestUtilsWalletStorage {
352
352
  let filePath: string
353
353
  let addLocalBackup = false
354
354
  let setActiveClient = false
355
+ let useMySQLConnectionForClient = false
355
356
  if (typeof args === 'string') {
356
357
  chain = args
357
358
  const env = _tu.getEnv(chain)
@@ -369,6 +370,7 @@ export abstract class TestUtilsWalletStorage {
369
370
  filePath = args.filePath
370
371
  addLocalBackup = args.addLocalBackup === true
371
372
  setActiveClient = args.setActiveClient === true
373
+ useMySQLConnectionForClient = args.useMySQLConnectionForClient === true
372
374
  }
373
375
 
374
376
  const databaseName = path.parse(filePath).name
@@ -380,12 +382,28 @@ export abstract class TestUtilsWalletStorage {
380
382
  })
381
383
  setup.localStorageIdentityKey = setup.storage.getActiveStore()
382
384
 
383
- const endpointUrl =
384
- chain === 'main'
385
- ? 'https://storage.babbage.systems'
386
- : 'https://staging-storage.babbage.systems'
385
+ let client: sdk.WalletStorageProvider
386
+ if (useMySQLConnectionForClient) {
387
+ const env = _tu.getEnv(chain)
388
+ if (!env.cloudMySQLConnection)
389
+ throw new sdk.WERR_INVALID_PARAMETER(
390
+ 'env.cloundMySQLConnection',
391
+ 'valid'
392
+ )
393
+ const connection = JSON.parse(env.cloudMySQLConnection)
394
+ client = new StorageKnex({
395
+ ...StorageKnex.defaultOptions(),
396
+ knex: _tu.createMySQLFromConnection(connection),
397
+ chain: env.chain
398
+ })
399
+ } else {
400
+ const endpointUrl =
401
+ chain === 'main'
402
+ ? 'https://storage.babbage.systems'
403
+ : 'https://staging-storage.babbage.systems'
387
404
 
388
- const client = new StorageClient(setup.wallet, endpointUrl)
405
+ client = new StorageClient(setup.wallet, endpointUrl)
406
+ }
389
407
  setup.clientStorageIdentityKey = (
390
408
  await client.makeAvailable()
391
409
  ).storageIdentityKey
@@ -2515,4 +2533,5 @@ export interface CreateTestWalletArgs {
2515
2533
  filePath: string
2516
2534
  addLocalBackup?: boolean
2517
2535
  setActiveClient?: boolean
2536
+ useMySQLConnectionForClient?: boolean
2518
2537
  }