diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/taler-wallet-core/src/db.ts | 2 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/internal-wallet-state.ts | 8 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/deposits.ts | 40 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/exchanges.ts | 60 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/operations/transactions.ts | 4 | ||||
| -rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 15 | 
6 files changed, 103 insertions, 26 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 69606b8ff..e3da35975 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -550,7 +550,7 @@ export interface ExchangeRecord {    /**     * Retry status for fetching updated information about the exchange.     */ -  retryInfo: RetryInfo; +  retryInfo?: RetryInfo;  }  /** diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts index 5ecf796ed..6b964cdf7 100644 --- a/packages/taler-wallet-core/src/internal-wallet-state.ts +++ b/packages/taler-wallet-core/src/internal-wallet-state.ts @@ -35,6 +35,7 @@ import {    AmountJson,    DenominationPubKey,    TalerProtocolTimestamp, +  CancellationToken,  } from "@gnu-taler/taler-util";  import { CryptoDispatcher } from "./crypto/workers/cryptoDispatcher.js";  import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; @@ -200,9 +201,14 @@ export interface InternalWalletState {    memoGetBalance: AsyncOpMemoSingle<BalancesResponse>;    memoProcessRefresh: AsyncOpMemoMap<void>;    memoProcessRecoup: AsyncOpMemoMap<void>; -  memoProcessDeposit: AsyncOpMemoMap<void>;    cryptoApi: TalerCryptoInterface; +  /** +   * Cancellation token for the currently running +   * deposit operation, if any. +   */ +  taskCancellationSourceForDeposit?: CancellationToken.Source; +    timerGroup: TimerGroup;    stopped: boolean; diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 501e9b76b..c11c45dc6 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -21,6 +21,7 @@ import {    AbsoluteTime,    AmountJson,    Amounts, +  CancellationToken,    canonicalJson,    codecForDepositSuccess,    ContractTerms, @@ -125,23 +126,34 @@ async function reportDepositGroupError(  export async function processDepositGroup(    ws: InternalWalletState,    depositGroupId: string, -  forceNow = false, +  options: { +    forceNow?: boolean; +    cancellationToken?: CancellationToken; +  } = {},  ): Promise<void> { -  await ws.memoProcessDeposit.memo(depositGroupId, async () => { -    const onOpErr = (err: TalerErrorDetail): Promise<void> => -      reportDepositGroupError(ws, depositGroupId, err); -    return await guardOperationException( -      async () => await processDepositGroupImpl(ws, depositGroupId, forceNow), -      onOpErr, -    ); -  }); +  if (ws.taskCancellationSourceForDeposit) { +    ws.taskCancellationSourceForDeposit.cancel(); +  } +  const onOpErr = (err: TalerErrorDetail): Promise<void> => +    reportDepositGroupError(ws, depositGroupId, err); +  return await guardOperationException( +    async () => await processDepositGroupImpl(ws, depositGroupId, options), +    onOpErr, +  );  } +/** + * @see {processDepositGroup} + */  async function processDepositGroupImpl(    ws: InternalWalletState,    depositGroupId: string, -  forceNow = false, +  options: { +    forceNow?: boolean; +    cancellationToken?: CancellationToken; +  } = {},  ): Promise<void> { +  const forceNow = options.forceNow ?? false;    const depositGroup = await ws.db      .mktx((x) => ({        depositGroups: x.depositGroups, @@ -170,6 +182,8 @@ async function processDepositGroupImpl(      "",    ); +  // Check for cancellation before expensive operations. +  options.cancellationToken?.throwIfCancelled();    const depositPermissions = await generateDepositPermissions(      ws,      depositGroup.payCoinSelection, @@ -196,9 +210,13 @@ async function processDepositGroupImpl(        denom_pub_hash: perm.h_denom,        merchant_pub: depositGroup.merchantPub,      }; +    // Check for cancellation before making network request. +    options.cancellationToken?.throwIfCancelled();      const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);      logger.info(`depositing to ${url}`); -    const httpResp = await ws.http.postJson(url.href, requestBody); +    const httpResp = await ws.http.postJson(url.href, requestBody, { +      cancellationToken: options.cancellationToken, +    });      await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());      await ws.db        .mktx((x) => ({ depositGroups: x.depositGroups })) diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 09449c875..fe1c9ef35 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -61,7 +61,11 @@ import {    readSuccessResponseTextOrThrow,  } from "../util/http.js";  import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; -import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; +import { +  initRetryInfo, +  RetryInfo, +  updateRetryInfoTimeout, +} from "../util/retries.js";  import {    WALLET_CACHE_BREAKER_CLIENT_VERSION,    WALLET_EXCHANGE_PROTOCOL_VERSION, @@ -102,7 +106,7 @@ function denominationRecordFromKeys(    return d;  } -async function handleExchangeUpdateError( +async function reportExchangeUpdateError(    ws: InternalWalletState,    baseUrl: string,    err: TalerErrorDetail, @@ -114,14 +118,44 @@ async function handleExchangeUpdateError(        if (!exchange) {          return;        } -      exchange.retryInfo.retryCounter++; -      updateRetryInfoTimeout(exchange.retryInfo);        exchange.lastError = err;        await tx.exchanges.put(exchange);      }); -  if (err) { -    ws.notify({ type: NotificationType.ExchangeOperationError, error: err }); -  } +  ws.notify({ type: NotificationType.ExchangeOperationError, error: err }); +} + +async function resetExchangeUpdateRetry( +  ws: InternalWalletState, +  baseUrl: string, +): Promise<void> { +  await ws.db +    .mktx((x) => ({ exchanges: x.exchanges })) +    .runReadWrite(async (tx) => { +      const exchange = await tx.exchanges.get(baseUrl); +      if (!exchange) { +        return; +      } +      delete exchange.lastError; +      exchange.retryInfo = initRetryInfo(); +      await tx.exchanges.put(exchange); +    }); +} + +async function incrementExchangeUpdateRetry( +  ws: InternalWalletState, +  baseUrl: string, +): Promise<void> { +  await ws.db +    .mktx((x) => ({ exchanges: x.exchanges })) +    .runReadWrite(async (tx) => { +      const exchange = await tx.exchanges.get(baseUrl); +      if (!exchange) { +        return; +      } +      delete exchange.lastError; +      exchange.retryInfo = RetryInfo.increment(exchange.retryInfo); +      await tx.exchanges.put(exchange); +    });  }  export function getExchangeRequestTimeout(): Duration { @@ -349,7 +383,7 @@ export async function updateExchangeFromUrl(    exchangeDetails: ExchangeDetailsRecord;  }> {    const onOpErr = (e: TalerErrorDetail): Promise<void> => -    handleExchangeUpdateError(ws, baseUrl, e); +    reportExchangeUpdateError(ws, baseUrl, e);    return await guardOperationException(      () => updateExchangeFromUrlImpl(ws, baseUrl, acceptedFormat, forceNow),      onOpErr, @@ -543,6 +577,12 @@ async function updateExchangeFromUrlImpl(      return { exchange, exchangeDetails };    } +  if (forceNow) { +    await resetExchangeUpdateRetry(ws, baseUrl); +  } else { +    await incrementExchangeUpdateRetry(ws, baseUrl); +  } +    logger.info("updating exchange /keys info");    const timeout = getExchangeRequestTimeout(); @@ -624,8 +664,8 @@ async function updateExchangeFromUrlImpl(          termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp.now(),        };        // FIXME: only update if pointer got updated -      r.lastError = undefined; -      r.retryInfo = initRetryInfo(); +      delete r.lastError; +      delete r.retryInfo;        r.lastUpdate = TalerProtocolTimestamp.now();        r.nextUpdate = keysInfo.expiry;        // New denominations might be available. diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index cb312154e..bb5306189 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -444,7 +444,9 @@ export async function retryTransaction(    switch (type) {      case TransactionType.Deposit:        const depositGroupId = rest[0]; -      processDepositGroup(ws, depositGroupId, true); +      processDepositGroup(ws, depositGroupId, { +        forceNow: true, +      });        break;      case TransactionType.Withdrawal:        const withdrawalGroupId = rest[0]; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 943051153..abd11faab 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -78,6 +78,7 @@ import {    URL,    WalletNotification,    Duration, +  CancellationToken,  } from "@gnu-taler/taler-util";  import { timeStamp } from "console";  import { @@ -271,9 +272,19 @@ async function processOnePendingOperation(      case PendingTaskType.ExchangeCheckRefresh:        await autoRefresh(ws, pending.exchangeBaseUrl);        break; -    case PendingTaskType.Deposit: -      await processDepositGroup(ws, pending.depositGroupId); +    case PendingTaskType.Deposit: { +      const cts = CancellationToken.create(); +      ws.taskCancellationSourceForDeposit = cts; +      try { +        await processDepositGroup(ws, pending.depositGroupId, { +          cancellationToken: cts.token, +        }); +      } finally { +        cts.dispose(); +        delete ws.taskCancellationSourceForDeposit; +      }        break; +    }      case PendingTaskType.Backup:        await processBackupForProvider(ws, pending.backupProviderBaseUrl);        break;  | 
