@bsv/wallet-toolbox 1.1.34 → 1.1.36
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/docs/client.md +215 -213
- package/docs/setup.md +2 -2
- package/docs/storage.md +140 -34
- package/docs/wallet.md +215 -213
- package/out/src/Setup.d.ts.map +1 -1
- package/out/src/Setup.js +2 -2
- package/out/src/Setup.js.map +1 -1
- package/out/src/sdk/WERR_errors.d.ts +1 -1
- package/out/src/sdk/WERR_errors.d.ts.map +1 -1
- package/out/src/sdk/WERR_errors.js +2 -2
- package/out/src/sdk/WERR_errors.js.map +1 -1
- package/out/src/sdk/WalletStorage.interfaces.d.ts +107 -0
- package/out/src/sdk/WalletStorage.interfaces.d.ts.map +1 -1
- package/out/src/sdk/index.d.ts +0 -2
- package/out/src/sdk/index.d.ts.map +1 -1
- package/out/src/sdk/index.js +0 -2
- package/out/src/sdk/index.js.map +1 -1
- package/out/src/services/__tests/verifyBeef.test.d.ts +2 -0
- package/out/src/services/__tests/verifyBeef.test.d.ts.map +1 -0
- package/out/src/services/__tests/verifyBeef.test.js +16 -0
- package/out/src/services/__tests/verifyBeef.test.js.map +1 -0
- package/out/src/services/providers/__tests/WhatsOnChain.test.js +11 -0
- package/out/src/services/providers/__tests/WhatsOnChain.test.js.map +1 -1
- package/out/src/storage/StorageReader.d.ts +1 -1
- package/out/src/storage/StorageReader.d.ts.map +1 -1
- package/out/src/storage/StorageReaderWriter.d.ts.map +1 -1
- package/out/src/storage/StorageReaderWriter.js +2 -1
- package/out/src/storage/StorageReaderWriter.js.map +1 -1
- package/out/src/storage/StorageSyncReader.d.ts +2 -18
- package/out/src/storage/StorageSyncReader.d.ts.map +1 -1
- package/out/src/storage/StorageSyncReader.js +1 -105
- package/out/src/storage/StorageSyncReader.js.map +1 -1
- package/out/src/storage/WalletStorageManager.d.ts +86 -10
- package/out/src/storage/WalletStorageManager.d.ts.map +1 -1
- package/out/src/storage/WalletStorageManager.js +239 -79
- package/out/src/storage/WalletStorageManager.js.map +1 -1
- package/out/src/storage/schema/KnexMigrations.d.ts.map +1 -1
- package/out/src/storage/schema/KnexMigrations.js +19 -0
- package/out/src/storage/schema/KnexMigrations.js.map +1 -1
- package/out/src/storage/schema/entities/SyncState.js +1 -1
- package/out/src/storage/schema/entities/User.d.ts +2 -2
- package/out/src/storage/schema/entities/User.d.ts.map +1 -1
- package/out/src/storage/schema/entities/User.js +1 -1
- package/out/src/storage/schema/entities/User.js.map +1 -1
- package/out/src/storage/schema/entities/__tests/SyncStateTests.test.js +2 -1
- package/out/src/storage/schema/entities/__tests/SyncStateTests.test.js.map +1 -1
- package/out/src/storage/schema/entities/__tests/usersTests.test.js +12 -6
- package/out/src/storage/schema/entities/__tests/usersTests.test.js.map +1 -1
- package/out/src/storage/schema/tables/User.d.ts +1 -1
- package/out/src/storage/schema/tables/User.d.ts.map +1 -1
- package/out/src/storage/sync/StorageMySQLDojoReader.d.ts +1 -1
- package/out/src/storage/sync/StorageMySQLDojoReader.d.ts.map +1 -1
- package/out/src/storage/sync/StorageMySQLDojoReader.js +4 -5
- package/out/src/storage/sync/StorageMySQLDojoReader.js.map +1 -1
- package/out/test/Wallet/sync/Wallet.updateWalletLegacyTestData.man.test.js +25 -13
- package/out/test/Wallet/sync/Wallet.updateWalletLegacyTestData.man.test.js.map +1 -1
- package/out/test/Wallet/sync/setActive.test.d.ts +2 -0
- package/out/test/Wallet/sync/setActive.test.d.ts.map +1 -0
- package/out/test/Wallet/sync/setActive.test.js +131 -0
- package/out/test/Wallet/sync/setActive.test.js.map +1 -0
- package/out/test/examples/{backup.test.d.ts → backup.man.test.d.ts} +1 -1
- package/out/test/examples/backup.man.test.d.ts.map +1 -0
- package/out/test/examples/{backup.test.js → backup.man.test.js} +1 -1
- package/out/test/examples/backup.man.test.js.map +1 -0
- package/out/test/examples/pushdrop.test.d.ts.map +1 -1
- package/out/test/examples/pushdrop.test.js +2 -6
- package/out/test/examples/pushdrop.test.js.map +1 -1
- package/out/test/storage/StorageMySQLDojoReader.man.test.js.map +1 -1
- package/out/test/storage/count.test.js +1 -0
- package/out/test/storage/count.test.js.map +1 -1
- package/out/test/storage/find.test.js +1 -0
- package/out/test/storage/find.test.js.map +1 -1
- package/out/test/storage/update.test.js +3 -1
- package/out/test/storage/update.test.js.map +1 -1
- package/out/test/storage/update2.test.js +5 -2
- package/out/test/storage/update2.test.js.map +1 -1
- package/out/test/utils/TestUtilsWalletStorage.d.ts +4 -0
- package/out/test/utils/TestUtilsWalletStorage.d.ts.map +1 -1
- package/out/test/utils/TestUtilsWalletStorage.js +10 -5
- package/out/test/utils/TestUtilsWalletStorage.js.map +1 -1
- package/out/test/wallet/sync/Wallet.sync.test.js +23 -8
- package/out/test/wallet/sync/Wallet.sync.test.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Setup.ts +3 -2
- package/src/sdk/WERR_errors.ts +3 -2
- package/src/sdk/WalletStorage.interfaces.ts +117 -0
- package/src/sdk/index.ts +0 -2
- package/src/services/__tests/verifyBeef.test.ts +18 -0
- package/src/services/providers/__tests/WhatsOnChain.test.ts +16 -1
- package/src/storage/StorageReader.ts +1 -1
- package/src/storage/StorageReaderWriter.ts +2 -1
- package/src/storage/StorageSyncReader.ts +2 -109
- package/src/storage/WalletStorageManager.ts +317 -87
- package/src/storage/schema/KnexMigrations.ts +23 -1
- package/src/storage/schema/entities/SyncState.ts +1 -1
- package/src/storage/schema/entities/User.ts +2 -2
- package/src/storage/schema/entities/__tests/SyncStateTests.test.ts +2 -1
- package/src/storage/schema/entities/__tests/usersTests.test.ts +12 -6
- package/src/storage/schema/tables/User.ts +1 -1
- package/src/storage/sync/StorageMySQLDojoReader.ts +8 -6
- package/test/Wallet/sync/Wallet.updateWalletLegacyTestData.man.test.ts +30 -13
- package/test/Wallet/sync/setActive.test.ts +147 -0
- package/test/examples/pushdrop.test.ts +3 -8
- package/test/storage/StorageMySQLDojoReader.man.test.ts +1 -1
- package/test/storage/count.test.ts +1 -0
- package/test/storage/find.test.ts +1 -0
- package/test/storage/update.test.ts +3 -1
- package/test/storage/update2.test.ts +5 -2
- package/test/utils/TestUtilsWalletStorage.ts +15 -5
- package/test/wallet/sync/Wallet.sync.test.ts +28 -8
- package/out/src/sdk/StorageSyncReader.d.ts +0 -121
- package/out/src/sdk/StorageSyncReader.d.ts.map +0 -1
- package/out/src/sdk/StorageSyncReader.js +0 -3
- package/out/src/sdk/StorageSyncReader.js.map +0 -1
- package/out/src/sdk/StorageSyncReaderWriter.d.ts +0 -89
- package/out/src/sdk/StorageSyncReaderWriter.d.ts.map +0 -1
- package/out/src/sdk/StorageSyncReaderWriter.js +0 -3
- package/out/src/sdk/StorageSyncReaderWriter.js.map +0 -1
- package/out/test/examples/backup.test.d.ts.map +0 -1
- package/out/test/examples/backup.test.js.map +0 -1
- package/src/sdk/StorageSyncReader.ts +0 -173
- package/src/sdk/StorageSyncReaderWriter.ts +0 -277
- /package/test/examples/{backup.test.ts → backup.man.test.ts} +0 -0
|
@@ -25,6 +25,19 @@ import {
|
|
|
25
25
|
TableUser,
|
|
26
26
|
wait
|
|
27
27
|
} from '../index.client'
|
|
28
|
+
import { WERR_INVALID_PARAMETER } from '../sdk'
|
|
29
|
+
|
|
30
|
+
class ManagedStorage {
|
|
31
|
+
isAvailable: boolean
|
|
32
|
+
isStorageProvider: boolean
|
|
33
|
+
settings?: TableSettings
|
|
34
|
+
user?: TableUser
|
|
35
|
+
|
|
36
|
+
constructor(public storage: sdk.WalletStorageProvider) {
|
|
37
|
+
this.isStorageProvider = storage.isStorageProvider()
|
|
38
|
+
this.isAvailable = false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
28
41
|
|
|
29
42
|
/**
|
|
30
43
|
* The `WalletStorageManager` class delivers authentication checking storage access to the wallet.
|
|
@@ -42,11 +55,41 @@ import {
|
|
|
42
55
|
* for these services.
|
|
43
56
|
*/
|
|
44
57
|
export class WalletStorageManager implements sdk.WalletStorage {
|
|
45
|
-
|
|
58
|
+
/**
|
|
59
|
+
* All configured stores including current active, backups, and conflicting actives.
|
|
60
|
+
*/
|
|
61
|
+
_stores: ManagedStorage[] = []
|
|
62
|
+
/**
|
|
63
|
+
* True if makeAvailable has been run and access to managed stores (active) is allowed
|
|
64
|
+
*/
|
|
65
|
+
_isAvailable: boolean = false
|
|
66
|
+
/**
|
|
67
|
+
* The current active store which is only enabled if the store's user record activeStorage property matches its settings record storageIdentityKey property
|
|
68
|
+
*/
|
|
69
|
+
_active?: ManagedStorage
|
|
70
|
+
/**
|
|
71
|
+
* Stores to which state is pushed by updateBackups.
|
|
72
|
+
*/
|
|
73
|
+
_backups?: ManagedStorage[]
|
|
74
|
+
/**
|
|
75
|
+
* Stores whose user record activeStorage property disagrees with the active store's user record activeStorage property.
|
|
76
|
+
*/
|
|
77
|
+
_conflictingActives?: ManagedStorage[]
|
|
78
|
+
/**
|
|
79
|
+
* identityKey is always valid, userId and isActive are valid only if _isAvailable
|
|
80
|
+
*/
|
|
46
81
|
_authId: sdk.AuthId
|
|
82
|
+
/**
|
|
83
|
+
* Configured services if any. If valid, shared with stores (which may ignore it).
|
|
84
|
+
*/
|
|
47
85
|
_services?: sdk.WalletServices
|
|
48
|
-
|
|
86
|
+
/**
|
|
87
|
+
* How many read access operations are pending
|
|
88
|
+
*/
|
|
49
89
|
_readerCount: number = 0
|
|
90
|
+
/**
|
|
91
|
+
* How many write access operations are pending
|
|
92
|
+
*/
|
|
50
93
|
_writerCount: number = 0
|
|
51
94
|
/**
|
|
52
95
|
* if true, allow only a single writer to proceed at a time.
|
|
@@ -69,9 +112,9 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
69
112
|
active?: sdk.WalletStorageProvider,
|
|
70
113
|
backups?: sdk.WalletStorageProvider[]
|
|
71
114
|
) {
|
|
72
|
-
|
|
73
|
-
if (active)
|
|
74
|
-
|
|
115
|
+
const stores = [...(backups || [])]
|
|
116
|
+
if (active) stores.unshift(active)
|
|
117
|
+
this._stores = stores.map(s => new ManagedStorage(s))
|
|
75
118
|
this._authId = { identityKey }
|
|
76
119
|
}
|
|
77
120
|
|
|
@@ -79,35 +122,153 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
79
122
|
return false
|
|
80
123
|
}
|
|
81
124
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
125
|
+
isAvailable(): boolean {
|
|
126
|
+
return this._isAvailable
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The active storage is "enabled" only if its `storageIdentityKey` matches the user's currently selected `activeStorage`,
|
|
131
|
+
* and only if there are no stores with conflicting `activeStorage` selections.
|
|
132
|
+
*
|
|
133
|
+
* A wallet may be created without including the user's currently selected active storage. This allows readonly access to their wallet data.
|
|
134
|
+
*
|
|
135
|
+
* In addition, if there are conflicting `activeStorage` selections among backup storage providers then the active remains disabled.
|
|
136
|
+
*/
|
|
137
|
+
get isActiveEnabled(): boolean {
|
|
138
|
+
return (
|
|
139
|
+
this._active !== undefined &&
|
|
140
|
+
this._active.settings!.storageIdentityKey ===
|
|
141
|
+
this._active.user!.activeStorage &&
|
|
142
|
+
this._conflictingActives !== undefined &&
|
|
143
|
+
this._conflictingActives.length === 0
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @returns true if at least one WalletStorageProvider has been added.
|
|
149
|
+
*/
|
|
150
|
+
canMakeAvailable(): boolean {
|
|
151
|
+
return this._stores.length > 0
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* This async function must be called after construction and before
|
|
156
|
+
* any other async function can proceed.
|
|
157
|
+
*
|
|
158
|
+
* Runs through `_stores` validating all properties and partitioning across `_active`, `_backups`, `_conflictingActives`.
|
|
159
|
+
*
|
|
160
|
+
* @throws WERR_INVALID_PARAMETER if canMakeAvailable returns false.
|
|
161
|
+
*
|
|
162
|
+
* @returns {TableSettings} from the active storage.
|
|
163
|
+
*/
|
|
164
|
+
async makeAvailable(): Promise<TableSettings> {
|
|
165
|
+
if (this._isAvailable) return this._active!.settings!
|
|
166
|
+
|
|
167
|
+
this._active = undefined
|
|
168
|
+
this._backups = []
|
|
169
|
+
this._conflictingActives = []
|
|
170
|
+
|
|
171
|
+
if (this._stores.length < 1)
|
|
172
|
+
throw new sdk.WERR_INVALID_PARAMETER(
|
|
173
|
+
'active',
|
|
174
|
+
'valid. Must add active storage provider to wallet.'
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// Initial backups. conflictingActives will be removed.
|
|
178
|
+
const backups: ManagedStorage[] = []
|
|
179
|
+
let i = -1
|
|
180
|
+
for (const store of this._stores) {
|
|
181
|
+
i++
|
|
182
|
+
if (!store.isAvailable || !store.settings || !store.user) {
|
|
183
|
+
// Validate all ManagedStorage properties.
|
|
184
|
+
store.settings = await store.storage.makeAvailable()
|
|
185
|
+
const r = await store.storage.findOrInsertUser(this._authId.identityKey)
|
|
186
|
+
store.user = r.user
|
|
187
|
+
store.isAvailable = true
|
|
188
|
+
}
|
|
189
|
+
if (!this._active)
|
|
190
|
+
// _stores[0] becomes the default active store. It may be replaced if it is not the user's "enabled" activeStorage and that store is found among the remainder (backups).
|
|
191
|
+
this._active = store
|
|
192
|
+
else {
|
|
193
|
+
const ua = store.user!.activeStorage
|
|
194
|
+
const si = store.settings!.storageIdentityKey
|
|
195
|
+
if (ua === si && !this.isActiveEnabled) {
|
|
196
|
+
// This store's user record selects it as an enabled active storage...
|
|
197
|
+
// swap the current not-enabled active for this storeage.
|
|
198
|
+
backups.push(this._active!)
|
|
199
|
+
this._active = store
|
|
200
|
+
} else {
|
|
201
|
+
// This store is a backup: Its user record selects some other storage as active.
|
|
202
|
+
backups.push(store)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Review backups, partition out conflicting actives.
|
|
208
|
+
const si = this._active!.settings?.storageIdentityKey
|
|
209
|
+
for (const store of backups) {
|
|
210
|
+
if (store.user!.activeStorage !== si) this._conflictingActives.push(store)
|
|
211
|
+
else this._backups.push(store)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this._isAvailable = true
|
|
215
|
+
this._authId.userId = this._active!.user!.userId
|
|
216
|
+
this._authId.isActive = this.isActiveEnabled
|
|
217
|
+
|
|
218
|
+
return this._active!.settings!
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private verifyActive(): ManagedStorage {
|
|
222
|
+
if (!this._active || !this._isAvailable)
|
|
223
|
+
throw new sdk.WERR_INVALID_OPERATION(
|
|
224
|
+
'An active WalletStorageProvider must be added to this WalletStorageManager and makeAvailable must be called.'
|
|
225
|
+
)
|
|
226
|
+
return this._active
|
|
85
227
|
}
|
|
86
228
|
|
|
87
229
|
async getAuth(mustBeActive?: boolean): Promise<sdk.AuthId> {
|
|
88
230
|
if (!this.isAvailable()) await this.makeAvailable()
|
|
89
|
-
const { user, isNew } = await this.getActive().findOrInsertUser(
|
|
90
|
-
this._authId.identityKey
|
|
91
|
-
)
|
|
92
|
-
if (!user)
|
|
93
|
-
throw new sdk.WERR_INVALID_PARAMETER('identityKey', 'exist on storage.')
|
|
94
|
-
this._authId.userId = user.userId
|
|
95
|
-
this._authId.isActive =
|
|
96
|
-
user.activeStorage === undefined ||
|
|
97
|
-
user.activeStorage === this.getSettings().storageIdentityKey
|
|
98
231
|
if (mustBeActive && !this._authId.isActive) throw new sdk.WERR_NOT_ACTIVE()
|
|
99
232
|
return this._authId
|
|
100
233
|
}
|
|
101
234
|
|
|
235
|
+
async getUserId(): Promise<number> {
|
|
236
|
+
return (await this.getAuth()).userId!
|
|
237
|
+
}
|
|
238
|
+
|
|
102
239
|
getActive(): sdk.WalletStorageProvider {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return this.
|
|
240
|
+
return this.verifyActive().storage
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getActiveSettings(): TableSettings {
|
|
244
|
+
return this.verifyActive().settings!
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getActiveUser(): TableUser {
|
|
248
|
+
return this.verifyActive().user!
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getActiveStore(): string {
|
|
252
|
+
return this.verifyActive().settings!.storageIdentityKey
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
getBackupStores(): string[] {
|
|
256
|
+
this.verifyActive()
|
|
257
|
+
return this._backups!.map(b => b.settings!.storageIdentityKey)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
getConflictingStores(): string[] {
|
|
261
|
+
this.verifyActive()
|
|
262
|
+
return this._conflictingActives!.map(b => b.settings!.storageIdentityKey)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
getAllStores(): string[] {
|
|
266
|
+
this.verifyActive()
|
|
267
|
+
return this._stores.map(b => b.settings!.storageIdentityKey)
|
|
108
268
|
}
|
|
109
269
|
|
|
110
270
|
async getActiveForWriter(): Promise<sdk.WalletStorageWriter> {
|
|
271
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
111
272
|
while (
|
|
112
273
|
this._storageProviderLocked ||
|
|
113
274
|
this._syncLocked ||
|
|
@@ -121,6 +282,7 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
121
282
|
}
|
|
122
283
|
|
|
123
284
|
async getActiveForReader(): Promise<sdk.WalletStorageReader> {
|
|
285
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
124
286
|
while (
|
|
125
287
|
this._storageProviderLocked ||
|
|
126
288
|
this._syncLocked ||
|
|
@@ -133,6 +295,7 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
133
295
|
}
|
|
134
296
|
|
|
135
297
|
async getActiveForSync(): Promise<sdk.WalletStorageSync> {
|
|
298
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
136
299
|
// Wait for a current sync task to complete...
|
|
137
300
|
while (this._syncLocked) {
|
|
138
301
|
await wait(100)
|
|
@@ -152,6 +315,7 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
152
315
|
}
|
|
153
316
|
|
|
154
317
|
async getActiveForStorageProvider(): Promise<StorageProvider> {
|
|
318
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
155
319
|
// Wait for a current storageProvider call to complete...
|
|
156
320
|
while (this._storageProviderLocked) {
|
|
157
321
|
await wait(100)
|
|
@@ -234,21 +398,19 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
234
398
|
return this.getActive().isStorageProvider()
|
|
235
399
|
}
|
|
236
400
|
|
|
237
|
-
isAvailable(): boolean {
|
|
238
|
-
return this.getActive().isAvailable()
|
|
239
|
-
}
|
|
240
|
-
|
|
241
401
|
async addWalletStorageProvider(
|
|
242
402
|
provider: sdk.WalletStorageProvider
|
|
243
403
|
): Promise<void> {
|
|
244
404
|
await provider.makeAvailable()
|
|
245
405
|
if (this._services) provider.setServices(this._services)
|
|
246
|
-
this.
|
|
406
|
+
this._stores.push(new ManagedStorage(provider))
|
|
407
|
+
this._isAvailable = false
|
|
408
|
+
await this.makeAvailable()
|
|
247
409
|
}
|
|
248
410
|
|
|
249
411
|
setServices(v: sdk.WalletServices) {
|
|
250
412
|
this._services = v
|
|
251
|
-
for (const store of this.
|
|
413
|
+
for (const store of this._stores) store.storage.setServices(v)
|
|
252
414
|
}
|
|
253
415
|
getServices(): sdk.WalletServices {
|
|
254
416
|
if (!this._services)
|
|
@@ -260,13 +422,6 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
260
422
|
return this.getActive().getSettings()
|
|
261
423
|
}
|
|
262
424
|
|
|
263
|
-
async makeAvailable(): Promise<TableSettings> {
|
|
264
|
-
return await this.runAsWriter(async writer => {
|
|
265
|
-
writer.makeAvailable()
|
|
266
|
-
return writer.getSettings()
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
|
|
270
425
|
async migrate(
|
|
271
426
|
storageName: string,
|
|
272
427
|
storageIdentityKey: string
|
|
@@ -277,9 +432,9 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
277
432
|
}
|
|
278
433
|
|
|
279
434
|
async destroy(): Promise<void> {
|
|
280
|
-
if (this.
|
|
435
|
+
if (this._stores.length < 1) return
|
|
281
436
|
return await this.runAsWriter(async writer => {
|
|
282
|
-
for (const store of this.
|
|
437
|
+
for (const store of this._stores) await store.storage.destroy()
|
|
283
438
|
})
|
|
284
439
|
}
|
|
285
440
|
|
|
@@ -415,21 +570,27 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
415
570
|
|
|
416
571
|
async syncFromReader(
|
|
417
572
|
identityKey: string,
|
|
418
|
-
reader:
|
|
419
|
-
|
|
573
|
+
reader: sdk.WalletStorageSyncReader,
|
|
574
|
+
activeSync?: sdk.WalletStorageSync,
|
|
575
|
+
log: string = ''
|
|
576
|
+
): Promise<{ inserts: number; updates: number; log: string }> {
|
|
420
577
|
const auth = await this.getAuth()
|
|
421
578
|
if (identityKey !== auth.identityKey) throw new sdk.WERR_UNAUTHORIZED()
|
|
422
579
|
|
|
423
580
|
const readerSettings = await reader.makeAvailable()
|
|
424
581
|
|
|
425
|
-
|
|
582
|
+
let inserts = 0,
|
|
583
|
+
updates = 0
|
|
584
|
+
|
|
585
|
+
log = await this.runAsSync(async sync => {
|
|
426
586
|
const writer = sync
|
|
427
587
|
const writerSettings = this.getSettings()
|
|
428
588
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
589
|
+
log += `syncFromReader from ${readerSettings.storageName} to ${writerSettings.storageName}\n`
|
|
590
|
+
|
|
591
|
+
let i = -1
|
|
432
592
|
for (;;) {
|
|
593
|
+
i++
|
|
433
594
|
const ss = await EntitySyncState.fromStorage(
|
|
434
595
|
writer,
|
|
435
596
|
identityKey,
|
|
@@ -440,43 +601,45 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
440
601
|
writerSettings.storageIdentityKey
|
|
441
602
|
)
|
|
442
603
|
const chunk = await reader.getSyncChunk(args)
|
|
604
|
+
if (chunk.user) {
|
|
605
|
+
// Merging state from a reader cannot update activeStorage
|
|
606
|
+
chunk.user.activeStorage = this._active!.user!.activeStorage
|
|
607
|
+
}
|
|
443
608
|
const r = await writer.processSyncChunk(args, chunk)
|
|
444
609
|
inserts += r.inserts
|
|
445
610
|
updates += r.updates
|
|
446
|
-
|
|
611
|
+
log += `chunk ${i} inserted ${r.inserts} updated ${r.updates} ${r.maxUpdated_at}\n`
|
|
447
612
|
if (r.done) break
|
|
448
613
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
})
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async updateBackups(activeSync?: sdk.WalletStorageSync) {
|
|
455
|
-
const auth = await this.getAuth()
|
|
456
|
-
return await this.runAsSync(async sync => {
|
|
457
|
-
for (const backup of this.stores.slice(1)) {
|
|
458
|
-
await this.syncToWriter(auth, backup, sync)
|
|
459
|
-
}
|
|
614
|
+
log += `syncFromReader complete: ${inserts} inserts, ${updates} updates\n`
|
|
615
|
+
return log
|
|
460
616
|
}, activeSync)
|
|
617
|
+
|
|
618
|
+
return { inserts, updates, log }
|
|
461
619
|
}
|
|
462
620
|
|
|
463
621
|
async syncToWriter(
|
|
464
622
|
auth: sdk.AuthId,
|
|
465
623
|
writer: sdk.WalletStorageProvider,
|
|
466
|
-
activeSync?: sdk.WalletStorageSync
|
|
467
|
-
|
|
624
|
+
activeSync?: sdk.WalletStorageSync,
|
|
625
|
+
log: string = ''
|
|
626
|
+
): Promise<{ inserts: number; updates: number; log: string }> {
|
|
468
627
|
const identityKey = auth.identityKey
|
|
469
628
|
|
|
470
629
|
const writerSettings = await writer.makeAvailable()
|
|
471
630
|
|
|
472
|
-
|
|
631
|
+
let inserts = 0,
|
|
632
|
+
updates = 0
|
|
633
|
+
|
|
634
|
+
log = await this.runAsSync(async sync => {
|
|
473
635
|
const reader = sync
|
|
474
|
-
const readerSettings =
|
|
636
|
+
const readerSettings = reader.getSettings()
|
|
475
637
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
638
|
+
log += `syncToWriter from ${readerSettings.storageName} to ${writerSettings.storageName}\n`
|
|
639
|
+
|
|
640
|
+
let i = -1
|
|
479
641
|
for (;;) {
|
|
642
|
+
i++
|
|
480
643
|
const ss = await EntitySyncState.fromStorage(
|
|
481
644
|
writer,
|
|
482
645
|
identityKey,
|
|
@@ -490,50 +653,117 @@ export class WalletStorageManager implements sdk.WalletStorage {
|
|
|
490
653
|
const r = await writer.processSyncChunk(args, chunk)
|
|
491
654
|
inserts += r.inserts
|
|
492
655
|
updates += r.updates
|
|
493
|
-
log +=
|
|
656
|
+
log += `chunk ${i} inserted ${r.inserts} updated ${r.updates} ${r.maxUpdated_at}\n`
|
|
494
657
|
if (r.done) break
|
|
495
658
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
659
|
+
log += `syncToWriter complete: ${inserts} inserts, ${updates} updates\n`
|
|
660
|
+
return log
|
|
661
|
+
}, activeSync)
|
|
662
|
+
|
|
663
|
+
return { inserts, updates, log }
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
async updateBackups(activeSync?: sdk.WalletStorageSync) {
|
|
667
|
+
const auth = await this.getAuth(true)
|
|
668
|
+
return await this.runAsSync(async sync => {
|
|
669
|
+
for (const backup of this._backups!) {
|
|
670
|
+
await this.syncToWriter(auth, backup.storage, sync)
|
|
671
|
+
}
|
|
499
672
|
}, activeSync)
|
|
500
673
|
}
|
|
501
674
|
|
|
502
675
|
/**
|
|
503
676
|
* Updates backups and switches to new active storage provider from among current backup providers.
|
|
504
677
|
*
|
|
678
|
+
* Also resolves conflicting actives.
|
|
679
|
+
*
|
|
505
680
|
* @param storageIdentityKey of current backup storage provider that is to become the new active provider.
|
|
506
681
|
*/
|
|
507
|
-
async setActive(storageIdentityKey: string): Promise<
|
|
508
|
-
|
|
509
|
-
|
|
682
|
+
async setActive(storageIdentityKey: string): Promise<string> {
|
|
683
|
+
if (!this.isAvailable()) await this.makeAvailable()
|
|
684
|
+
|
|
685
|
+
// Confirm a valid storageIdentityKey: must match one of the _stores.
|
|
686
|
+
const newActiveIndex = this._stores.findIndex(
|
|
687
|
+
s => s.settings!.storageIdentityKey === storageIdentityKey
|
|
510
688
|
)
|
|
511
689
|
if (newActiveIndex < 0)
|
|
512
690
|
throw new sdk.WERR_INVALID_PARAMETER(
|
|
513
691
|
'storageIdentityKey',
|
|
514
|
-
`registered with this "WalletStorageManager"
|
|
692
|
+
`registered with this "WalletStorageManager". ${storageIdentityKey} does not match any managed store.`
|
|
515
693
|
)
|
|
516
|
-
|
|
694
|
+
|
|
695
|
+
const identityKey = (await this.getAuth()).identityKey
|
|
696
|
+
const newActive = this._stores[newActiveIndex]
|
|
697
|
+
|
|
698
|
+
let log = `setActive to ${newActive.settings!.storageName}`
|
|
699
|
+
|
|
700
|
+
if (storageIdentityKey === this.getActiveStore() && this.isActiveEnabled)
|
|
517
701
|
/** Setting the current active as the new active is a permitted no-op. */
|
|
518
|
-
return
|
|
702
|
+
return log + ` unchanged\n`
|
|
519
703
|
|
|
520
|
-
|
|
521
|
-
const newActive = this.stores[newActiveIndex]
|
|
522
|
-
const newActiveStorageIdentityKey = (await newActive.makeAvailable())
|
|
523
|
-
.storageIdentityKey
|
|
704
|
+
log += '\n'
|
|
524
705
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
706
|
+
log += await this.runAsSync(async sync => {
|
|
707
|
+
let log = ''
|
|
708
|
+
|
|
709
|
+
if (this._conflictingActives!.length > 0) {
|
|
710
|
+
// Handle case where new active is current active to resolve conflicts.
|
|
711
|
+
// And where new active is one of the current conflict actives.
|
|
712
|
+
this._conflictingActives!.push(this._active!)
|
|
713
|
+
// Remove the new active from conflicting actives and
|
|
714
|
+
// set new active as the conflicting active that matches the target `storageIdentityKey`
|
|
715
|
+
this._conflictingActives = this._conflictingActives!.filter(ca => {
|
|
716
|
+
const isNewActive =
|
|
717
|
+
ca.settings!.storageIdentityKey === storageIdentityKey
|
|
718
|
+
return !isNewActive
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
// Merge state from conflicting actives into `newActive`.
|
|
722
|
+
for (const conflict of this._conflictingActives) {
|
|
723
|
+
const sfr = await this.syncToWriter(
|
|
724
|
+
{ identityKey, userId: newActive.user!.userId, isActive: false },
|
|
725
|
+
newActive.storage,
|
|
726
|
+
conflict.storage
|
|
727
|
+
)
|
|
728
|
+
log += sfr.log
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// If there were conflicting actives,
|
|
733
|
+
// Push state merged from all merged actives into newActive to all stores other than the now single active.
|
|
734
|
+
// Otherwise,
|
|
735
|
+
// Push state from current active to all other stores.
|
|
736
|
+
const backupSource =
|
|
737
|
+
this._conflictingActives!.length > 0 ? newActive : this._active!
|
|
738
|
+
|
|
739
|
+
for (const store of this._stores) {
|
|
740
|
+
// Update all store's user records to reflect new active store
|
|
741
|
+
await store.storage.setActive(
|
|
742
|
+
{ identityKey, userId: store.user!.userId },
|
|
743
|
+
storageIdentityKey
|
|
744
|
+
)
|
|
745
|
+
// Update cached user.activeStorage of all stores
|
|
746
|
+
store.user!.activeStorage = storageIdentityKey
|
|
747
|
+
|
|
748
|
+
if (
|
|
749
|
+
store.settings!.storageIdentityKey !==
|
|
750
|
+
backupSource.settings!.storageIdentityKey
|
|
751
|
+
) {
|
|
752
|
+
const stwr = await this.syncToWriter(
|
|
753
|
+
{ identityKey, userId: store.user!.userId, isActive: false },
|
|
754
|
+
store.storage,
|
|
755
|
+
backupSource.storage
|
|
756
|
+
)
|
|
757
|
+
log += stwr.log
|
|
758
|
+
}
|
|
536
759
|
}
|
|
760
|
+
|
|
761
|
+
this._isAvailable = false
|
|
762
|
+
await this.makeAvailable()
|
|
763
|
+
|
|
764
|
+
return log
|
|
537
765
|
})
|
|
766
|
+
|
|
767
|
+
return log
|
|
538
768
|
}
|
|
539
769
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import { Knex } from 'knex'
|
|
3
|
-
import { sdk } from '../../index.all'
|
|
3
|
+
import { sdk, StorageKnex } from '../../index.all'
|
|
4
4
|
import { DBType } from '../StorageReader'
|
|
5
5
|
|
|
6
6
|
interface Migration {
|
|
@@ -91,6 +91,28 @@ export class KnexMigrations implements MigrationSource<string> {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
migrations['2025-02-22-001 nonNULL activeStorage'] = {
|
|
95
|
+
async up(knex) {
|
|
96
|
+
const storage = new StorageKnex({
|
|
97
|
+
...StorageKnex.defaultOptions(),
|
|
98
|
+
chain: <sdk.Chain>chain,
|
|
99
|
+
knex
|
|
100
|
+
})
|
|
101
|
+
const settings = await storage.makeAvailable()
|
|
102
|
+
await knex.raw(
|
|
103
|
+
`update users set activeStorage = '${settings.storageIdentityKey}' where activeStorage is NULL`
|
|
104
|
+
)
|
|
105
|
+
await knex.schema.alterTable('users', table => {
|
|
106
|
+
table.string('activeStorage').notNullable().alter()
|
|
107
|
+
})
|
|
108
|
+
},
|
|
109
|
+
async down(knex) {
|
|
110
|
+
await knex.schema.alterTable('users', table => {
|
|
111
|
+
table.string('activeStorage').nullable().alter()
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
94
116
|
migrations['2025-01-21-001 add activeStorage to users'] = {
|
|
95
117
|
async up(knex) {
|
|
96
118
|
await knex.schema.alterTable('users', table => {
|
|
@@ -309,7 +309,7 @@ export class EntitySyncState extends EntityBase<TableSyncState> {
|
|
|
309
309
|
): sdk.RequestSyncChunkArgs {
|
|
310
310
|
const a: sdk.RequestSyncChunkArgs = {
|
|
311
311
|
identityKey: forIdentityKey,
|
|
312
|
-
maxRoughSize: maxRoughSize ||
|
|
312
|
+
maxRoughSize: maxRoughSize || 10000000,
|
|
313
313
|
maxItems: maxItems || 1000,
|
|
314
314
|
offsets: [],
|
|
315
315
|
since: this.when,
|
|
@@ -18,7 +18,7 @@ export class EntityUser extends EntityBase<TableUser> {
|
|
|
18
18
|
created_at: now,
|
|
19
19
|
updated_at: now,
|
|
20
20
|
identityKey: '',
|
|
21
|
-
activeStorage:
|
|
21
|
+
activeStorage: ''
|
|
22
22
|
}
|
|
23
23
|
)
|
|
24
24
|
}
|
|
@@ -54,7 +54,7 @@ export class EntityUser extends EntityBase<TableUser> {
|
|
|
54
54
|
get activeStorage() {
|
|
55
55
|
return this.api.activeStorage
|
|
56
56
|
}
|
|
57
|
-
set activeStorage(v: string
|
|
57
|
+
set activeStorage(v: string) {
|
|
58
58
|
this.api.activeStorage = v
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -146,7 +146,8 @@ describe('SyncState class method tests', () => {
|
|
|
146
146
|
userId: 1,
|
|
147
147
|
identityKey: 'testIdentityKey',
|
|
148
148
|
created_at: new Date(), // Add required property
|
|
149
|
-
updated_at: new Date() // Add required property
|
|
149
|
+
updated_at: new Date(), // Add required property
|
|
150
|
+
activeStorage: ''
|
|
150
151
|
},
|
|
151
152
|
provenTxs: [],
|
|
152
153
|
provenTxReqs: [],
|