gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: cache denomination lookups


From: gnunet
Subject: [taler-wallet-core] branch master updated: cache denomination lookups
Date: Thu, 13 Jan 2022 15:49:48 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new cd2473e1 cache denomination lookups
cd2473e1 is described below

commit cd2473e1ade13ca43d8f6fafaa2e8d3c3675bfd8
Author: Florian Dold <florian@dold.me>
AuthorDate: Thu Jan 13 12:08:31 2022 +0100

    cache denomination lookups
---
 packages/taler-wallet-core/src/common.ts           |  77 ++++++++-
 .../taler-wallet-core/src/operations/pending.ts    | 181 +++++++++++----------
 .../taler-wallet-core/src/operations/withdraw.ts   |  15 +-
 packages/taler-wallet-core/src/wallet.ts           |  54 ++++--
 4 files changed, 210 insertions(+), 117 deletions(-)

diff --git a/packages/taler-wallet-core/src/common.ts 
b/packages/taler-wallet-core/src/common.ts
index 5b61ef14..5cbfae4b 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -20,7 +20,7 @@
  * management, etc.).
  *
  * Some operations can be accessed via this state object.  This allows mutual
- * recursion between operations, without having cycling dependencies between
+ * recursion between operations, without having cyclic dependencies between
  * the respective TypeScript files.
  *
  * (You can think of this as a "header file" for the wallet implementation.)
@@ -29,7 +29,13 @@
 /**
  * Imports.
  */
-import { WalletNotification, BalancesResponse } from "@gnu-taler/taler-util";
+import {
+  WalletNotification,
+  BalancesResponse,
+  AmountJson,
+  DenominationPubKey,
+  Timestamp,
+} from "@gnu-taler/taler-util";
 import { CryptoApi } from "./crypto/workers/cryptoApi.js";
 import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from 
"./db.js";
 import { PendingOperationsResponse } from "./pending-types.js";
@@ -119,6 +125,64 @@ export interface RecoupOperations {
   ): Promise<void>;
 }
 
+export interface DenomInfo {
+  /**
+   * Value of one coin of the denomination.
+   */
+  value: AmountJson;
+
+  /**
+   * The denomination public key.
+   */
+  denomPub: DenominationPubKey;
+
+  /**
+   * Hash of the denomination public key.
+   * Stored in the database for faster lookups.
+   */
+  denomPubHash: string;
+
+  /**
+   * Fee for withdrawing.
+   */
+  feeWithdraw: AmountJson;
+
+  /**
+   * Fee for depositing.
+   */
+  feeDeposit: AmountJson;
+
+  /**
+   * Fee for refreshing.
+   */
+  feeRefresh: AmountJson;
+
+  /**
+   * Fee for refunding.
+   */
+  feeRefund: AmountJson;
+
+  /**
+   * Validity start date of the denomination.
+   */
+  stampStart: Timestamp;
+
+  /**
+   * Date after which the currency can't be withdrawn anymore.
+   */
+  stampExpireWithdraw: Timestamp;
+
+  /**
+   * Date after the denomination officially doesn't exist anymore.
+   */
+  stampExpireLegal: Timestamp;
+
+  /**
+   * Data after which coins of this denomination can't be deposited anymore.
+   */
+  stampExpireDeposit: Timestamp;
+}
+
 export type NotificationListener = (n: WalletNotification) => void;
 
 /**
@@ -162,6 +226,15 @@ export interface InternalWalletState {
   merchantOps: MerchantOperations;
   reserveOps: ReserveOperations;
 
+  getDenomInfo(
+    ws: InternalWalletState,
+    tx: GetReadWriteAccess<{
+      denominations: typeof WalletStoresV1.denominations;
+    }>,
+    exchangeBaseUrl: string,
+    denomPubHash: string,
+  ): Promise<DenomInfo | undefined>;
+
   db: DbAccess<typeof WalletStoresV1>;
   http: HttpRequestLibrary;
 
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index b2f13625..7e82236f 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -35,10 +35,7 @@ import {
   PendingTaskType,
   ReserveType,
 } from "../pending-types.js";
-import {
-  getTimestampNow,
-  Timestamp,
-} from "@gnu-taler/taler-util";
+import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../common.js";
 import { GetReadOnlyAccess } from "../util/query.js";
 
@@ -74,35 +71,36 @@ async function gatherReservePending(
   now: Timestamp,
   resp: PendingOperationsResponse,
 ): Promise<void> {
-  await tx.reserves.indexes.byStatus
-    .iter(OperationStatus.Pending)
-    .forEach((reserve) => {
-      const reserveType = reserve.bankInfo
-        ? ReserveType.TalerBankWithdraw
-        : ReserveType.Manual;
-      switch (reserve.reserveStatus) {
-        case ReserveRecordStatus.DORMANT:
-          // nothing to report as pending
-          break;
-        case ReserveRecordStatus.WAIT_CONFIRM_BANK:
-        case ReserveRecordStatus.QUERYING_STATUS:
-        case ReserveRecordStatus.REGISTERING_BANK:
-          resp.pendingOperations.push({
-            type: PendingTaskType.Reserve,
-            givesLifeness: true,
-            timestampDue: reserve.retryInfo.nextRetry,
-            stage: reserve.reserveStatus,
-            timestampCreated: reserve.timestampCreated,
-            reserveType,
-            reservePub: reserve.reservePub,
-            retryInfo: reserve.retryInfo,
-          });
-          break;
-        default:
-          // FIXME: report problem!
-          break;
-      }
-    });
+  const reserves = await tx.reserves.indexes.byStatus.getAll(
+    OperationStatus.Pending,
+  );
+  for (const reserve of reserves) {
+    const reserveType = reserve.bankInfo
+      ? ReserveType.TalerBankWithdraw
+      : ReserveType.Manual;
+    switch (reserve.reserveStatus) {
+      case ReserveRecordStatus.DORMANT:
+        // nothing to report as pending
+        break;
+      case ReserveRecordStatus.WAIT_CONFIRM_BANK:
+      case ReserveRecordStatus.QUERYING_STATUS:
+      case ReserveRecordStatus.REGISTERING_BANK:
+        resp.pendingOperations.push({
+          type: PendingTaskType.Reserve,
+          givesLifeness: true,
+          timestampDue: reserve.retryInfo.nextRetry,
+          stage: reserve.reserveStatus,
+          timestampCreated: reserve.timestampCreated,
+          reserveType,
+          reservePub: reserve.reservePub,
+          retryInfo: reserve.retryInfo,
+        });
+        break;
+      default:
+        // FIXME: report problem!
+        break;
+    }
+  }
 }
 
 async function gatherRefreshPending(
@@ -110,26 +108,27 @@ async function gatherRefreshPending(
   now: Timestamp,
   resp: PendingOperationsResponse,
 ): Promise<void> {
-  await tx.refreshGroups.indexes.byStatus
-    .iter(OperationStatus.Pending)
-    .forEach((r) => {
-      if (r.timestampFinished) {
-        return;
-      }
-      if (r.frozen) {
-        return;
-      }
-      resp.pendingOperations.push({
-        type: PendingTaskType.Refresh,
-        givesLifeness: true,
-        timestampDue: r.retryInfo.nextRetry,
-        refreshGroupId: r.refreshGroupId,
-        finishedPerCoin: r.statusPerCoin.map(
-          (x) => x === RefreshCoinStatus.Finished,
-        ),
-        retryInfo: r.retryInfo,
-      });
+  const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(
+    OperationStatus.Pending,
+  );
+  for (const r of refreshGroups) {
+    if (r.timestampFinished) {
+      return;
+    }
+    if (r.frozen) {
+      return;
+    }
+    resp.pendingOperations.push({
+      type: PendingTaskType.Refresh,
+      givesLifeness: true,
+      timestampDue: r.retryInfo.nextRetry,
+      refreshGroupId: r.refreshGroupId,
+      finishedPerCoin: r.statusPerCoin.map(
+        (x) => x === RefreshCoinStatus.Finished,
+      ),
+      retryInfo: r.retryInfo,
     });
+  }
 }
 
 async function gatherWithdrawalPending(
@@ -140,31 +139,32 @@ async function gatherWithdrawalPending(
   now: Timestamp,
   resp: PendingOperationsResponse,
 ): Promise<void> {
-  await tx.withdrawalGroups.indexes.byStatus
-    .iter(OperationStatus.Pending)
-    .forEachAsync(async (wsr) => {
-      if (wsr.timestampFinish) {
-        return;
-      }
-      let numCoinsWithdrawn = 0;
-      let numCoinsTotal = 0;
-      await tx.planchets.indexes.byGroup
-        .iter(wsr.withdrawalGroupId)
-        .forEach((x) => {
-          numCoinsTotal++;
-          if (x.withdrawalDone) {
-            numCoinsWithdrawn++;
-          }
-        });
-      resp.pendingOperations.push({
-        type: PendingTaskType.Withdraw,
-        givesLifeness: true,
-        timestampDue: wsr.retryInfo.nextRetry,
-        withdrawalGroupId: wsr.withdrawalGroupId,
-        lastError: wsr.lastError,
-        retryInfo: wsr.retryInfo,
+  const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
+    OperationStatus.Pending,
+  );
+  for (const wsr of wsrs) {
+    if (wsr.timestampFinish) {
+      return;
+    }
+    let numCoinsWithdrawn = 0;
+    let numCoinsTotal = 0;
+    await tx.planchets.indexes.byGroup
+      .iter(wsr.withdrawalGroupId)
+      .forEach((x) => {
+        numCoinsTotal++;
+        if (x.withdrawalDone) {
+          numCoinsWithdrawn++;
+        }
       });
+    resp.pendingOperations.push({
+      type: PendingTaskType.Withdraw,
+      givesLifeness: true,
+      timestampDue: wsr.retryInfo.nextRetry,
+      withdrawalGroupId: wsr.withdrawalGroupId,
+      lastError: wsr.lastError,
+      retryInfo: wsr.retryInfo,
     });
+  }
 }
 
 async function gatherProposalPending(
@@ -197,22 +197,23 @@ async function gatherDepositPending(
   now: Timestamp,
   resp: PendingOperationsResponse,
 ): Promise<void> {
-  await tx.depositGroups.indexes.byStatus
-    .iter(OperationStatus.Pending)
-    .forEach((dg) => {
-      if (dg.timestampFinished) {
-        return;
-      }
-      const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
-      resp.pendingOperations.push({
-        type: PendingTaskType.Deposit,
-        givesLifeness: true,
-        timestampDue,
-        depositGroupId: dg.depositGroupId,
-        lastError: dg.lastError,
-        retryInfo: dg.retryInfo,
-      });
+  const dgs = await tx.depositGroups.indexes.byStatus.getAll(
+    OperationStatus.Pending,
+  );
+  for (const dg of dgs) {
+    if (dg.timestampFinished) {
+      return;
+    }
+    const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow();
+    resp.pendingOperations.push({
+      type: PendingTaskType.Deposit,
+      givesLifeness: true,
+      timestampDue,
+      depositGroupId: dg.depositGroupId,
+      lastError: dg.lastError,
+      retryInfo: dg.retryInfo,
     });
+  }
 }
 
 async function gatherTipPending(
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index dd8a90ad..f9eeb02c 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -975,13 +975,13 @@ async function processWithdrawGroupImpl(
 
 export async function getExchangeWithdrawalInfo(
   ws: InternalWalletState,
-  baseUrl: string,
+  exchangeBaseUrl: string,
   amount: AmountJson,
 ): Promise<ExchangeWithdrawDetails> {
   const { exchange, exchangeDetails } =
-    await ws.exchangeOps.updateExchangeFromUrl(ws, baseUrl);
-  await updateWithdrawalDenoms(ws, baseUrl);
-  const denoms = await getCandidateWithdrawalDenoms(ws, baseUrl);
+    await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl);
+  await updateWithdrawalDenoms(ws, exchangeBaseUrl);
+  const denoms = await getCandidateWithdrawalDenoms(ws, exchangeBaseUrl);
   const selectedDenoms = selectWithdrawalDenominations(amount, denoms);
   const exchangeWireAccounts: string[] = [];
   for (const account of exchangeDetails.wireInfo.accounts) {
@@ -1006,9 +1006,10 @@ export async function getExchangeWithdrawalInfo(
   const possibleDenoms = await ws.db
     .mktx((x) => ({ denominations: x.denominations }))
     .runReadOnly(async (tx) => {
-      return tx.denominations.indexes.byExchangeBaseUrl
-        .iter()
-        .filter((d) => d.isOffered);
+      const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
+        exchangeBaseUrl,
+      );
+      return ds.filter((x) => x.isOffered);
     });
 
   let versionMatch;
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 3d83ec21..c5eb0e65 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -103,6 +103,7 @@ import {
   processReserve,
 } from "./operations/reserves.js";
 import {
+  DenomInfo,
   ExchangeOperations,
   InternalWalletState,
   MerchantInfo,
@@ -186,13 +187,12 @@ import {
   OpenedPromise,
   openPromise,
 } from "./util/promiseUtils.js";
-import { DbAccess } from "./util/query.js";
+import { DbAccess, GetReadWriteAccess } from "./util/query.js";
 import {
   HttpRequestLibrary,
   readSuccessResponseJsonOrThrow,
 } from "./util/http.js";
 import { getMerchantInfo } from "./operations/merchants.js";
-import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
 
 const builtinAuditors: AuditorTrustRecord[] = [
   {
@@ -506,24 +506,24 @@ async function listKnownBankAccounts(
   ws: InternalWalletState,
   currency?: string,
 ): Promise<KnownBankAccounts> {
-  const accounts: PaytoUri[] = []
+  const accounts: PaytoUri[] = [];
   await ws.db
     .mktx((x) => ({
       reserves: x.reserves,
     }))
     .runReadOnly(async (tx) => {
-      const reservesRecords = await tx.reserves.iter().toArray()
+      const reservesRecords = await tx.reserves.iter().toArray();
       for (const r of reservesRecords) {
         if (currency && currency !== r.currency) {
-          continue
+          continue;
         }
-        const payto = r.senderWire ? parsePaytoUri(r.senderWire) : undefined
+        const payto = r.senderWire ? parsePaytoUri(r.senderWire) : undefined;
         if (payto) {
-          accounts.push(payto)
+          accounts.push(payto);
         }
       }
-    })
-  return { accounts }
+    });
+  return { accounts };
 }
 
 async function getExchanges(
@@ -785,9 +785,8 @@ async function dispatchRequestInternal(
       return res;
     }
     case "getWithdrawalDetailsForAmount": {
-      const req = codecForGetWithdrawalDetailsForAmountRequest().decode(
-        payload,
-      );
+      const req =
+        codecForGetWithdrawalDetailsForAmountRequest().decode(payload);
       return await getWithdrawalDetailsForAmount(
         ws,
         req.exchangeBaseUrl,
@@ -810,9 +809,8 @@ async function dispatchRequestInternal(
       return await applyRefund(ws, req.talerRefundUri);
     }
     case "acceptBankIntegratedWithdrawal": {
-      const req = codecForAcceptBankIntegratedWithdrawalRequest().decode(
-        payload,
-      );
+      const req =
+        codecForAcceptBankIntegratedWithdrawalRequest().decode(payload);
       return await acceptWithdrawal(
         ws,
         req.talerWithdrawUri,
@@ -1048,7 +1046,7 @@ export async function handleCoreApiRequest(
       try {
         logger.error("Caught unexpected exception:");
         logger.error(e.stack);
-      } catch (e) { }
+      } catch (e) {}
       return {
         type: "error",
         operation,
@@ -1133,7 +1131,8 @@ export class Wallet {
 class InternalWalletStateImpl implements InternalWalletState {
   memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
   memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
-  memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> = new 
AsyncOpMemoSingle();
+  memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> =
+    new AsyncOpMemoSingle();
   memoGetBalance: AsyncOpMemoSingle<BalancesResponse> = new 
AsyncOpMemoSingle();
   memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
   memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap();
@@ -1169,7 +1168,10 @@ class InternalWalletStateImpl implements 
InternalWalletState {
 
   reserveOps: ReserveOperations = {
     processReserve: processReserve,
-  }
+  };
+
+  // FIXME: Use an LRU cache here.
+  private denomCache: Record<string, DenomInfo> = {};
 
   /**
    * Promises that are waiting for a particular resource.
@@ -1193,6 +1195,22 @@ class InternalWalletStateImpl implements 
InternalWalletState {
     this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
   }
 
+  async getDenomInfo(
+    ws: InternalWalletState,
+    tx: GetReadWriteAccess<{
+      denominations: typeof WalletStoresV1.denominations;
+    }>,
+    exchangeBaseUrl: string,
+    denomPubHash: string,
+  ): Promise<DenomInfo | undefined> {
+    const key = `${exchangeBaseUrl}:${denomPubHash}`;
+    const cached = this.denomCache[key];
+    if (cached) {
+      return cached;
+    }
+    return await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
+  }
+
   notify(n: WalletNotification): void {
     logger.trace("Notification", n);
     for (const l of this.listeners) {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]