commit ad39479e953e9703ca609dccd95b9029ad29ddf4
parent ff97f1f6f251851f643b874edcbb07d4661aee7b
Author: Florian Dold <florian@dold.me>
Date: Wed, 4 Jun 2025 00:49:21 +0200
wallet-core: improve deposit long-polling, try periodically even when kyc-check isn't satisfied
Diffstat:
2 files changed, 87 insertions(+), 46 deletions(-)
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
@@ -1079,29 +1079,29 @@ export type SlateRecord = Omit<TokenRecord, "tokenIssueSig">;
*/
export type DbWalletCoinHistoryItem =
| {
- type: "withdraw";
- transactionId: TransactionIdStr;
- }
+ type: "withdraw";
+ transactionId: TransactionIdStr;
+ }
| {
- type: "spend";
- transactionId: TransactionIdStr;
- amount: AmountString;
- }
+ type: "spend";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
| {
- type: "refresh";
- transactionId: TransactionIdStr;
- amount: AmountString;
- }
+ type: "refresh";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
| {
- type: "recoup";
- transactionId: TransactionIdStr;
- amount: AmountString;
- }
+ type: "recoup";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
| {
- type: "refund";
- transactionId: TransactionIdStr;
- amount: AmountString;
- };
+ type: "refund";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ };
/**
* History event for a coin from the wallet's perspective.
@@ -1573,9 +1573,9 @@ export enum ConfigRecordKey {
*/
export type ConfigRecord =
| {
- key: ConfigRecordKey.WalletBackupState;
- value: WalletBackupConfState;
- }
+ key: ConfigRecordKey.WalletBackupState;
+ value: WalletBackupConfState;
+ }
| { key: ConfigRecordKey.CurrencyDefaultsApplied; value: boolean }
| { key: ConfigRecordKey.TestLoopTx; value: number }
| { key: ConfigRecordKey.LastInitInfo; value: DbProtocolTimestamp }
@@ -1863,15 +1863,15 @@ export enum BackupProviderStateTag {
export type BackupProviderState =
| {
- tag: BackupProviderStateTag.Provisional;
- }
+ tag: BackupProviderStateTag.Provisional;
+ }
| {
- tag: BackupProviderStateTag.Ready;
- nextBackupTimestamp: DbPreciseTimestamp;
- }
+ tag: BackupProviderStateTag.Ready;
+ nextBackupTimestamp: DbPreciseTimestamp;
+ }
| {
- tag: BackupProviderStateTag.Retrying;
- };
+ tag: BackupProviderStateTag.Retrying;
+ };
export interface BackupProviderRecord {
/**
@@ -2036,6 +2036,11 @@ export interface DepositGroupRecord {
timestampFinished: DbPreciseTimestamp | undefined;
+ /**
+ * When did the wallet last try a deposit request?
+ */
+ timestampLastDepositAttempt: DbPreciseTimestamp | undefined;
+
operationStatus: DepositOperationStatus;
statusPerCoin?: DepositElementStatus[];
@@ -2872,12 +2877,13 @@ export const WalletStoresV1 = {
versionAdded: 17,
},
),
- byPurchaseIdAndChoiceIndex: describeIndex("byPurchaseIdAndChoiceIndex", [
- "purchaseId",
- "choiceIndex",
- ], {
- versionAdded: 17,
- }),
+ byPurchaseIdAndChoiceIndex: describeIndex(
+ "byPurchaseIdAndChoiceIndex",
+ ["purchaseId", "choiceIndex"],
+ {
+ versionAdded: 17,
+ },
+ ),
},
),
slates: describeStore(
@@ -2887,12 +2893,13 @@ export const WalletStoresV1 = {
versionAdded: 16,
}),
{
- byPurchaseIdAndChoiceIndex: describeIndex("byPurchaseIdAndChoiceIndex", [
- "purchaseId",
- "choiceIndex",
- ], {
- versionAdded: 17,
- }),
+ byPurchaseIdAndChoiceIndex: describeIndex(
+ "byPurchaseIdAndChoiceIndex",
+ ["purchaseId", "choiceIndex"],
+ {
+ versionAdded: 17,
+ },
+ ),
byPurchaseIdAndChoiceIndexAndOutputIndex: describeIndex(
"byPurchaseIdAndChoiceIndexAndOutputIndex",
["purchaseId", "choiceIndex", "outputIndex"],
@@ -3511,9 +3518,7 @@ export async function importDb(db: IDBDatabase, dumpJson: any): Promise<void> {
export interface FixupDescription {
name: string;
- fn(
- tx: WalletDbReadOnlyTransaction<WalletDbStoresArr>
- ): Promise<void>;
+ fn(tx: WalletDbReadOnlyTransaction<WalletDbStoresArr>): Promise<void>;
}
/**
@@ -3729,7 +3734,7 @@ export async function openStoredBackupsDatabase(
idbFactory,
TALER_WALLET_STORED_BACKUPS_DB_NAME,
1,
- () => { },
+ () => {},
onStoredBackupsDbUpgradeNeeded,
);
@@ -3757,7 +3762,7 @@ export async function openTalerDatabase(
idbFactory,
TALER_WALLET_META_DB_NAME,
1,
- () => { },
+ () => {},
onMetaDbUpgradeNeeded,
);
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
@@ -1052,6 +1052,20 @@ async function processDepositGroupPendingKyc(
const { depositGroupId } = depositGroup;
const ctx = new DepositTransactionContext(wex, depositGroupId);
+ if (
+ depositGroup.timestampLastDepositAttempt == null ||
+ AbsoluteTime.isExpired(
+ AbsoluteTime.addDuration(
+ timestampAbsoluteFromDb(depositGroup.timestampLastDepositAttempt),
+ Duration.fromSpec({ minutes: 2 }),
+ ),
+ )
+ ) {
+ logger.info(
+ `deposit group is in pending(kyc), but trying deposit anyway after two minutes since last attempt`,
+ );
+ return processDepositGroupPendingDeposit(wex, depositGroup);
+ }
const kycInfo = depositGroup.kycInfo;
if (!kycInfo) {
@@ -1067,6 +1081,7 @@ async function processDepositGroupPendingKyc(
`kyc-check/${kycInfo.paytoHash}`,
kycInfo.exchangeBaseUrl,
);
+ url.searchParams.set("lpt", "3");
logger.info(`kyc url ${url.href}`);
const kycStatusRes = await cancelableLongPoll(wex, url, {
headers: {
@@ -1114,6 +1129,7 @@ async function processDepositGroupPendingKyc(
codecForAccountKycStatus(),
);
logger.info(`kyc still pending (HTTP 202): ${j2s(statusResp)}`);
+ return TaskRunResult.longpollReturnedPending();
} else {
throwUnexpectedRequestError(
kycStatusRes,
@@ -1556,6 +1572,14 @@ async function processDepositGroupTrack(
}
}
+/**
+ * Try to deposit coins with the exchange.
+ *
+ * May either be called directly when the deposit group is
+ * in the pending(deposit) state or indirectly when the deposit
+ * group is in a KYC state but wants to try deposit anyway (in case KYC
+ * is for another operation).
+ */
async function processDepositGroupPendingDeposit(
wex: WalletExecutionContext,
depositGroup: DepositGroupRecord,
@@ -1677,6 +1701,17 @@ async function processDepositGroupPendingDeposit(
}
}
+ await wex.db.runReadWriteTx({ storeNames: ["depositGroups"] }, async (tx) => {
+ const dg = await tx.depositGroups.get(depositGroup.depositGroupId);
+ if (!dg) {
+ return;
+ }
+ dg.timestampLastDepositAttempt = timestampPreciseToDb(
+ TalerPreciseTimestamp.now(),
+ );
+ await tx.depositGroups.put(dg);
+ });
+
// FIXME: Cache these!
const depositPermissions = await generateDepositPermissions(
wex,
@@ -2297,6 +2332,7 @@ export async function createDepositGroup(
payto_uri: req.depositPaytoUri,
salt: wireSalt,
},
+ timestampLastDepositAttempt: undefined,
operationStatus: DepositOperationStatus.PendingDeposit,
infoPerExchange,
};