commit 56956b1740b188a1cc0efc354eec831c0fae76dc
parent 8f457f16b136898c268ad3897a8bea866801c239
Author: Florian Dold <florian@dold.me>
Date: Tue, 16 Sep 2025 19:25:11 +0200
wallet-core: testing request to run fixups manually
Diffstat:
6 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -3693,6 +3693,15 @@ export const codecForTestingGetDenomStatsRequest =
.property("exchangeBaseUrl", codecForCanonBaseUrl())
.build("TestingGetDenomStatsRequest");
+export interface RunFixupRequest {
+ id: string;
+}
+
+export const codecForRunFixupRequest = (): Codec<RunFixupRequest> =>
+ buildCodecForObject<RunFixupRequest>()
+ .property("id", codecForString())
+ .build("RunFixupRequest");
+
export interface WithdrawalExchangeAccountDetails {
/**
* Payto URI to credit the exchange.
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
@@ -1801,15 +1801,6 @@ export interface WithdrawalGroupRecord {
*/
denomsSel?: DenomSelectionState;
- /**
- * UID of the denomination selection.
- *
- * Used for merging backups.
- *
- * FIXME: Should this not also include a timestamp for more logical merging?
- */
- denomSelUid: string;
-
abortReason?: TalerErrorDetail;
failReason?: TalerErrorDetail;
}
@@ -3671,6 +3662,9 @@ async function fixup20250915TopsBlunder(
if (!exchRec) {
continue;
}
+ logger.info(
+ `have exchange ${exch} in update state ${exchRec.updateStatus}`,
+ );
exchRec.lastUpdate = undefined;
exchRec.lastKeysEtag = undefined;
switch (exchRec.updateStatus) {
@@ -3691,13 +3685,19 @@ async function fixup20250915TopsBlunder(
for (const exch of exchangeUrls) {
const wgs =
await tx.withdrawalGroups.indexes.byExchangeBaseUrl.getAll(exch);
+ logger.info(
+ `have ${wgs.length} withdrawal transactions that might need fixup`,
+ );
for (const wg of wgs) {
+ logger.info(`status ${wg.status}`);
+ logger.info(`denom sel ${j2s(wg.denomsSel)}`);
if (wg.status !== WithdrawalGroupStatus.Done) {
continue;
}
if (wg.denomsSel?.selectedDenoms.length != 0) {
continue;
}
+ logger.info(`updating withdrawal group status`);
wg.status = WithdrawalGroupStatus.PendingQueryingStatus;
await tx.withdrawalGroups.put(wg);
}
diff --git a/packages/taler-wallet-core/src/dev-experiments.ts b/packages/taler-wallet-core/src/dev-experiments.ts
@@ -352,7 +352,6 @@ async function addFakeTx(
}
await wex.db.runAllStoresReadWriteTx({}, async (tx) => {
await tx.withdrawalGroups.add({
- denomSelUid: encodeCrock(getRandomBytes(32)),
reservePriv: reservePair.priv,
reservePub: reservePair.pub,
secretSeed,
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -141,6 +141,7 @@ import {
RemoveGlobalCurrencyAuditorRequest,
RemoveGlobalCurrencyExchangeRequest,
RetryTransactionRequest,
+ RunFixupRequest,
SetCoinSuspendedRequest,
SetWalletDeviceIdRequest,
SharePaymentRequest,
@@ -317,6 +318,7 @@ export enum WalletApiOperation {
TestingResetAllRetries = "testingResetAllRetries",
TestingWaitExchangeWalletKyc = "testingWaitWalletKyc",
TestingPlanMigrateExchangeBaseUrl = "testingPlanMigrateExchangeBaseUrl",
+ TestingRunFixup = "testingRunFixup",
// Hints
@@ -1382,6 +1384,12 @@ export type TestingGetDenomStatsOp = {
response: TestingGetDenomStatsResponse;
};
+export type TestingRunFixupOp = {
+ op: WalletApiOperation.TestingRunFixup;
+ request: RunFixupRequest;
+ response: EmptyObject;
+};
+
/**
* Set a coin as (un-)suspended.
* Suspended coins won't be used for payments.
@@ -1510,6 +1518,7 @@ export type WalletOperations = {
[WalletApiOperation.RemoveGlobalCurrencyExchange]: RemoveGlobalCurrencyExchangeOp;
[WalletApiOperation.ListAssociatedRefreshes]: ListAssociatedRefreshesOp;
[WalletApiOperation.TestingGetDenomStats]: TestingGetDenomStatsOp;
+ [WalletApiOperation.TestingRunFixup]: TestingRunFixupOp;
[WalletApiOperation.TestingPing]: TestingPingOp;
[WalletApiOperation.Shutdown]: ShutdownOp;
[WalletApiOperation.PrepareBankIntegratedWithdrawal]: PrepareBankIntegratedWithdrawalOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
@@ -108,6 +108,7 @@ import {
RecoverStoredBackupRequest,
RemoveGlobalCurrencyAuditorRequest,
RemoveGlobalCurrencyExchangeRequest,
+ RunFixupRequest,
ScopeType,
SharePaymentRequest,
SharePaymentResult,
@@ -209,6 +210,7 @@ import {
codecForRemoveGlobalCurrencyExchangeRequest,
codecForResumeTransaction,
codecForRetryTransactionRequest,
+ codecForRunFixupRequest,
codecForSetCoinSuspendedRequest,
codecForSetWalletDeviceIdRequest,
codecForSharePaymentRequest,
@@ -290,6 +292,7 @@ import {
openTalerDatabase,
timestampAbsoluteFromDb,
timestampProtocolToDb,
+ walletDbFixups,
} from "./db.js";
import {
checkDepositGroup,
@@ -1813,6 +1816,23 @@ export async function handleHintApplicationResumed(
return {};
}
+export async function handleTestingRunFixup(
+ wex: WalletExecutionContext,
+ req: RunFixupRequest,
+): Promise<EmptyObject> {
+ for (const fixup of walletDbFixups) {
+ if (fixup.name != req.id) {
+ continue;
+ }
+ await wex.db.runAllStoresReadWriteTx({}, async (tx) => {
+ await fixup.fn(tx);
+ await rematerializeTransactions(wex, tx);
+ });
+ return {};
+ }
+ throw Error("fixup not found");
+}
+
async function handleGetVersion(
wex: WalletExecutionContext,
): Promise<WalletCoreVersion> {
@@ -1859,6 +1879,10 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = {
codec: codecForEmptyObject(),
handler: handleHintApplicationResumed,
},
+ [WalletApiOperation.TestingRunFixup]: {
+ codec: codecForRunFixupRequest(),
+ handler: handleTestingRunFixup,
+ },
[WalletApiOperation.AbortTransaction]: {
codec: codecForAbortTransaction(),
handler: handleAbortTransaction,
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
@@ -3522,7 +3522,6 @@ export async function internalPrepareCreateWithdrawalGroup(
}
let initialDenomSel: DenomSelectionState | undefined;
- const denomSelUid = encodeCrock(getRandomBytes(16));
if (amount !== undefined && exchangeBaseUrl !== undefined) {
initialDenomSel = await getInitialDenomsSelection(
@@ -3534,7 +3533,6 @@ export async function internalPrepareCreateWithdrawalGroup(
}
const withdrawalGroup: WithdrawalGroupRecord = {
- denomSelUid,
// next fields will be undefined if exchange or amount is not specified
denomsSel: initialDenomSel,
exchangeBaseUrl: exchangeBaseUrl,