commit e224380d3fa7bde5453ca80ab87be0202d800cc3 parent 3cee4fb6db58cee4d4128ddfca98c2700f5927cd Author: Florian Dold <florian@dold.me> Date: Fri, 27 Feb 2026 00:03:10 +0100 harness,wallet-core: test and fix deletion of exchange signing keys on purge Diffstat:
7 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-management.ts b/packages/taler-harness/src/integrationtests/test-exchange-management.ts @@ -65,6 +65,12 @@ export async function runExchangeManagementTest( t.assertDeepEqual(exchangesListResult3.exchanges.length, 0); + const diag = await walletClient.call(WalletApiOperation.GetDiagnostics, {}); + t.assertDeepEqual(diag.idbObjectStoreCounts?.exchanges, 0); + t.assertDeepEqual(diag.idbObjectStoreCounts?.denominations, 0); + t.assertDeepEqual(diag.idbObjectStoreCounts?.denominationFamilies, 0); + t.assertDeepEqual(diag.idbObjectStoreCounts?.exchangeSignKeys, 0); + // Check for regression: Can we re-add a deleted exchange? await walletClient.call(WalletApiOperation.UpdateExchangeEntry, { diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -4069,6 +4069,10 @@ export interface TestingGetDenomStatsResponse { export interface TestingGetDiagnosticsResponse { version: 0; + /** + * Statistics about the size of object stores. + */ + idbObjectStoreCounts?: Record<string, number>; exchangeEntries: { exchangeBaseUrl: string; numDenoms: number; diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts @@ -3286,6 +3286,7 @@ export const WalletStoresV1 = { keyPath: ["exchangeDetailsRowId", "signkeyPub"], }), { + // Caution: By historical accident, the index is over an array. byExchangeDetailsRowId: describeIndex("byExchangeDetailsRowId", [ "exchangeDetailsRowId", ]), diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -3153,7 +3153,9 @@ async function purgeExchange( } await tx.exchangeDetails.delete(r.rowId); const signkeyRecs = - await tx.exchangeSignKeys.indexes.byExchangeDetailsRowId.getAll(r.rowId); + await tx.exchangeSignKeys.indexes.byExchangeDetailsRowId.getAll([ + r.rowId, + ]); for (const rec of signkeyRecs) { await tx.exchangeSignKeys.delete([r.rowId, rec.signkeyPub]); } diff --git a/packages/taler-wallet-core/src/query.ts b/packages/taler-wallet-core/src/query.ts @@ -461,6 +461,7 @@ export interface StoreReadOnlyAccessor<RecordType, IndexMap> { count?: number, ): Promise<RecordType[]>; iter(query?: IDBValidKey): ResultStream<RecordType>; + count(query?: IDBValidKey): Promise<number>; indexes: GetIndexReadOnlyAccess<RecordType, IndexMap>; } @@ -481,6 +482,7 @@ export interface StoreReadWriteAccessor<RecordType, IndexMap> { put(r: RecordType, key?: IDBValidKey): Promise<InsertResponse>; add(r: RecordType, key?: IDBValidKey): Promise<InsertResponse>; delete(key: IDBValidKey): Promise<void>; + count(query?: IDBValidKey): Promise<number>; indexes: GetIndexReadWriteAccess<RecordType, IndexMap>; } @@ -882,6 +884,12 @@ function makeTxClientContext( const req = tx.objectStore(storeName).delete(k); return requestToPromise(req, internalContext); }, + count(query) { + internalContext.throwIfInactive(); + internalContext.storesAccessed.add(storeName); + const req = tx.objectStore(storeName).count(query); + return requestToPromise(req, internalContext); + }, }; } return ctx; diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -1546,10 +1546,10 @@ export type TestingSetTimetravelOp = { }; /** - * Add an offset to the wallet's internal time. + * Get database statistics. */ export type TestingGetDbStats = { - op: WalletApiOperation.TestingSetTimetravel; + op: WalletApiOperation.TestingGetDbStats; request: EmptyObject; response: any; }; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -282,7 +282,6 @@ import { setDangerousTimetravel, setGlobalLogLevelFromString, stringifyScopeInfo, - succeedOrThrow, validateIban, } from "@gnu-taler/taler-util"; import { @@ -2056,8 +2055,16 @@ export async function handleGetDiagnostics( wex: WalletExecutionContext, req: EmptyObject, ): Promise<TestingGetDiagnosticsResponse> { + const cnt: Record<string, number> = {}; const exchangeEntries: TestingGetDiagnosticsResponse["exchangeEntries"] = []; await wex.db.runAllStoresReadOnlyTx({}, async (tx) => { + cnt["coinAvailability"] = await tx.coinAvailability.count(); + cnt["coins"] = await tx.coins.count(); + cnt["denominationFamilies"] = await tx.denominationFamilies.count(); + cnt["denominations"] = await tx.denominations.count(); + cnt["exchangeDetails"] = await tx.exchangeDetails.count(); + cnt["exchangeSignKeys"] = await tx.exchangeSignKeys.count(); + cnt["exchanges"] = await tx.exchanges.count(); for (const exch of await tx.exchanges.getAll()) { const denoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll( exch.baseUrl, @@ -2083,6 +2090,7 @@ export async function handleGetDiagnostics( }); return { version: 0, + idbObjectStoreCounts: cnt, exchangeEntries, }; }