gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core: implement remain


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: implement remaining DD48 calls, test
Date: Tue, 16 Jan 2024 13:15:45 +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 1c286ebb2 wallet-core: implement remaining DD48 calls, test
1c286ebb2 is described below

commit 1c286ebb2f1c817f5362517d47466c39826c8699
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Jan 16 13:15:40 2024 +0100

    wallet-core: implement remaining DD48 calls, test
---
 .../src/integrationtests/test-wallet-dd48.ts       | 178 +++++++++++++++++++++
 .../src/integrationtests/testrunner.ts             |   2 +
 packages/taler-util/src/taler-error-codes.ts       |   8 +
 packages/taler-util/src/wallet-types.ts            |  63 ++++++--
 packages/taler-wallet-core/src/db.ts               |   8 +-
 .../taler-wallet-core/src/operations/exchanges.ts  | 164 ++++++++++++-------
 packages/taler-wallet-core/src/util/query.ts       |  10 ++
 packages/taler-wallet-core/src/wallet-api-types.ts |  37 ++++-
 packages/taler-wallet-core/src/wallet.ts           |  22 ++-
 9 files changed, 410 insertions(+), 82 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-wallet-dd48.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-dd48.ts
new file mode 100644
index 000000000..c4fe40586
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-wallet-dd48.ts
@@ -0,0 +1,178 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+  ExchangeService,
+  FakebankService,
+  GlobalTestState,
+  WalletClient,
+  WalletService,
+  setupDb,
+} from "../harness/harness.js";
+import {
+  ExchangeEntryStatus,
+  NotificationType,
+  TalerError,
+  TalerErrorCode,
+  WalletNotification,
+  j2s,
+} from "@gnu-taler/taler-util";
+import { withdrawViaBankV2 } from "../harness/helpers.js";
+
+/**
+ * Test for DD48 notifications.
+ */
+export async function runWalletDd48Test(t: GlobalTestState) {
+  // Set up test environment
+
+  const db = await setupDb(t);
+
+  const bank = await FakebankService.create(t, {
+    allowRegistrations: true,
+    currency: "TESTKUDOS",
+    database: db.connStr,
+    httpPort: 8082,
+  });
+
+  const exchange = ExchangeService.create(t, {
+    name: "testexchange-1",
+    currency: "TESTKUDOS",
+    httpPort: 8081,
+    database: db.connStr,
+  });
+
+  const exchangeBankAccount = await bank.createExchangeAccount(
+    "myexchange",
+    "x",
+  );
+  exchange.addBankAccount("1", exchangeBankAccount);
+
+  bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
+
+  await bank.start();
+
+  await bank.pingUntilAvailable();
+
+  const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => 
x("TESTKUDOS"));
+  exchange.addCoinConfigList(coinConfig);
+
+  await exchange.start();
+  await exchange.pingUntilAvailable();
+
+  const walletService = new WalletService(t, {
+    name: "wallet",
+    useInMemoryDb: true,
+  });
+  await walletService.start();
+  await walletService.pingUntilAvailable();
+
+  const allNotifications: WalletNotification[] = [];
+
+  const walletClient = new WalletClient({
+    unixPath: walletService.socketPath,
+    onNotification(n) {
+      console.log("got notification", n);
+      allNotifications.push(n);
+    },
+  });
+  await walletClient.connect();
+  await walletClient.client.call(WalletApiOperation.InitWallet, {
+    skipDefaults: true,
+  });
+
+  await walletClient.call(WalletApiOperation.AddExchange, {
+    exchangeBaseUrl: exchange.baseUrl,
+  });
+
+  {
+    const exchangeEntry = await walletClient.call(
+      WalletApiOperation.GetExchangeEntryByUrl,
+      {
+        exchangeBaseUrl: exchange.baseUrl,
+      },
+    );
+
+    t.assertDeepEqual(
+      exchangeEntry.exchangeEntryStatus,
+      ExchangeEntryStatus.Ephemeral,
+    );
+
+    const resources = await walletClient.call(
+      WalletApiOperation.GetExchangeResources,
+      {
+        exchangeBaseUrl: exchange.baseUrl,
+      },
+    );
+    t.assertDeepEqual(resources.hasResources, false);
+  }
+
+  const wres = await withdrawViaBankV2(t, {
+    walletClient,
+    amount: "TESTKUDOS:20",
+    bank,
+    exchange,
+  });
+
+  await wres.withdrawalFinishedCond;
+
+  const exchangeEntry = await walletClient.call(
+    WalletApiOperation.GetExchangeEntryByUrl,
+    {
+      exchangeBaseUrl: exchange.baseUrl,
+    },
+  );
+
+  t.assertDeepEqual(
+    exchangeEntry.exchangeEntryStatus,
+    ExchangeEntryStatus.Used,
+  );
+
+  t.assertTrue(
+    !!allNotifications.find(
+      (x) =>
+        x.type === NotificationType.ExchangeStateTransition &&
+        x.oldExchangeState == null &&
+        x.newExchangeState.exchangeEntryStatus ===
+          ExchangeEntryStatus.Ephemeral,
+    ),
+  );
+
+  console.log(j2s(allNotifications));
+
+  const delErr = await t.assertThrowsAsync(async () => {
+    await walletClient.call(WalletApiOperation.DeleteExchange, {
+      exchangeBaseUrl: exchange.baseUrl,
+    });
+  });
+
+  t.assertTrue(delErr instanceof TalerError);
+  t.assertDeepEqual(
+    delErr.errorDetail.code,
+    TalerErrorCode.WALLET_EXCHANGE_ENTRY_USED,
+  );
+
+  await walletClient.call(WalletApiOperation.DeleteExchange, {
+    exchangeBaseUrl: exchange.baseUrl,
+    purge: true,
+  });
+}
+
+runWalletDd48Test.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index 6a8eb9504..6ab87c756 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -98,6 +98,7 @@ import { runAgeRestrictionsDepositTest } from 
"./test-age-restrictions-deposit.j
 import { runWithdrawalConversionTest } from "./test-withdrawal-conversion.js";
 import { runPaymentDeletedTest } from "./test-payment-deleted.js";
 import { runWithdrawalNotifyBeforeTxTest } from 
"./test-withdrawal-notify-before-tx.js";
+import { runWalletDd48Test } from "./test-wallet-dd48.js";
 
 /**
  * Test runner.
@@ -185,6 +186,7 @@ const allTests: TestMainFunction[] = [
   runWalletGenDbTest,
   runLibeufinBankTest,
   runPaymentDeletedTest,
+  runWalletDd48Test,
 ];
 
 export interface TestRunSpec {
diff --git a/packages/taler-util/src/taler-error-codes.ts 
b/packages/taler-util/src/taler-error-codes.ts
index 9dd965d1b..6b9362329 100644
--- a/packages/taler-util/src/taler-error-codes.ts
+++ b/packages/taler-util/src/taler-error-codes.ts
@@ -3904,6 +3904,14 @@ export enum TalerErrorCode {
   WALLET_EXCHANGE_UNAVAILABLE = 7032,
 
 
+  /**
+   * An exchange entry is still used by the exchange, thus it can't be deleted 
without purging.
+   * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  WALLET_EXCHANGE_ENTRY_USED = 7033,
+
+
   /**
    * We encountered a timeout with our payment backend.
    * Returned with an HTTP status code of #MHD_HTTP_GATEWAY_TIMEOUT (504).
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 583d5dff5..52156cf9e 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -577,11 +577,11 @@ export interface CoinDumpJson {
     withdrawal_reserve_pub: string | undefined;
     coin_status: CoinStatus;
     spend_allocation:
-    | {
-      id: string;
-      amount: AmountString;
-    }
-    | undefined;
+      | {
+          id: string;
+          amount: AmountString;
+        }
+      | undefined;
     /**
      * Information about the age restriction
      */
@@ -944,14 +944,14 @@ export interface PreparePayResultAlreadyConfirmed {
 }
 
 export interface BankWithdrawDetails {
-  status: WithdrawalOperationStatus,
+  status: WithdrawalOperationStatus;
   amount: AmountJson;
   senderWire?: string;
   suggestedExchange?: string;
   confirmTransferUrl?: string;
   wireTypes: string[];
-  operationId: string,
-  apiBaseUrl: string,
+  operationId: string;
+  apiBaseUrl: string;
 }
 
 export interface AcceptWithdrawalResponse {
@@ -1701,6 +1701,31 @@ export const codecForUpdateExchangeEntryRequest =
       .property("exchangeBaseUrl", codecForString())
       .build("UpdateExchangeEntryRequest");
 
+export interface GetExchangeResourcesRequest {
+  exchangeBaseUrl: string;
+}
+
+export const codecForGetExchangeResourcesRequest =
+  (): Codec<GetExchangeResourcesRequest> =>
+    buildCodecForObject<GetExchangeResourcesRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .build("GetExchangeResourcesRequest");
+
+export interface GetExchangeResourcesResponse {
+  hasResources: boolean;
+}
+
+export interface DeleteExchangeRequest {
+  exchangeBaseUrl: string;
+  purge?: boolean;
+}
+
+export const codecForDeleteExchangeRequest = (): Codec<DeleteExchangeRequest> 
=>
+  buildCodecForObject<DeleteExchangeRequest>()
+    .property("exchangeBaseUrl", codecForString())
+    .property("purge", codecOptional(codecForBoolean()))
+    .build("DeleteExchangeRequest");
+
 export interface ForceExchangeUpdateRequest {
   exchangeBaseUrl: string;
 }
@@ -1810,7 +1835,10 @@ export const codecForGetWithdrawalDetailsForUri =
     buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
       .property("talerWithdrawUri", codecForString())
       .property("restrictAge", codecOptional(codecForNumber()))
-      .property("notifyChangeFromPendingTimeoutMs", 
codecOptional(codecForNumber()))
+      .property(
+        "notifyChangeFromPendingTimeoutMs",
+        codecOptional(codecForNumber()),
+      )
       .build("GetWithdrawalDetailsForUriRequest");
 
 export interface ListKnownBankAccountsRequest {
@@ -2191,7 +2219,7 @@ export interface TxIdResponse {
 
 export interface WithdrawUriInfoResponse {
   operationId: string;
-  status: WithdrawalOperationStatus,
+  status: WithdrawalOperationStatus;
   confirmTransferUrl?: string;
   amount: AmountString;
   defaultExchangeBaseUrl?: string;
@@ -2203,12 +2231,15 @@ export const codecForWithdrawUriInfoResponse =
     buildCodecForObject<WithdrawUriInfoResponse>()
       .property("operationId", codecForString())
       .property("confirmTransferUrl", codecOptional(codecForString()))
-      .property("status", codecForEither(
-        codecForConstString("pending"),
-        codecForConstString("selected"),
-        codecForConstString("aborted"),
-        codecForConstString("confirmed"),
-      ))
+      .property(
+        "status",
+        codecForEither(
+          codecForConstString("pending"),
+          codecForConstString("selected"),
+          codecForConstString("aborted"),
+          codecForConstString("confirmed"),
+        ),
+      )
       .property("amount", codecForAmountString())
       .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
       .property("possibleExchanges", codecForList(codecForExchangeListItem()))
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 5a412fb27..73739c19c 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -149,7 +149,7 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
  * backwards-compatible way or object stores and indices
  * are added.
  */
-export const WALLET_DB_MINOR_VERSION = 1;
+export const WALLET_DB_MINOR_VERSION = 2;
 
 declare const symDbProtocolTimestamp: unique symbol;
 
@@ -2383,6 +2383,9 @@ export const WalletStoresV1 = {
       autoIncrement: true,
     }),
     {
+      byExchangeBaseUrl: describeIndex("byExchangeBaseUrl", "exchangeBaseUrl", 
{
+        versionAdded: 2,
+      }),
       byPointer: describeIndex(
         "byDetailsPointer",
         ["exchangeBaseUrl", "currency", "masterPublicKey"],
@@ -2461,6 +2464,9 @@ export const WalletStoresV1 = {
     }),
     {
       byStatus: describeIndex("byStatus", "status"),
+      byExchangeBaseUrl: describeIndex("byExchangeBaseUrl", "exchangeBaseUrl", 
{
+        versionAdded: 2,
+      }),
       byTalerWithdrawUri: describeIndex(
         "byTalerWithdrawUri",
         "wgInfo.bankInfo.talerWithdrawUri",
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index ca3ea8af6..f983a7c4d 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -27,6 +27,7 @@ import {
   AbsoluteTime,
   Amounts,
   CancellationToken,
+  DeleteExchangeRequest,
   DenomKeyType,
   DenomOperationMap,
   DenominationInfo,
@@ -43,6 +44,7 @@ import {
   ExchangesListResponse,
   FeeDescription,
   GetExchangeEntryByUrlRequest,
+  GetExchangeResourcesResponse,
   GetExchangeTosResult,
   GlobalFees,
   LibtoolVersion,
@@ -63,7 +65,6 @@ import {
   WireInfo,
   canonicalizeBaseUrl,
   codecForExchangeKeysJson,
-  durationFromSpec,
   encodeCrock,
   hashDenomPub,
   j2s,
@@ -86,12 +87,10 @@ import {
 import {
   ExchangeEntryDbRecordStatus,
   ExchangeEntryDbUpdateStatus,
-  OpenedPromise,
   PendingTaskType,
   WalletDbReadWriteTransaction,
   createTimeline,
   isWithdrawableDenom,
-  openPromise,
   selectBestForOverlappingDenominations,
   selectMinimumFee,
   timestampOptionalAbsoluteFromDb,
@@ -100,9 +99,14 @@ import {
   timestampPreciseToDb,
   timestampProtocolToDb,
 } from "../index.js";
-import { CancelFn, InternalWalletState } from "../internal-wallet-state.js";
+import { InternalWalletState } from "../internal-wallet-state.js";
 import { checkDbInvariant } from "../util/invariants.js";
-import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js";
+import {
+  DbReadOnlyTransaction,
+  DbReadOnlyTransactionArr,
+  GetReadOnlyAccess,
+  GetReadWriteAccess,
+} from "../util/query.js";
 import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
 import {
   TaskIdentifiers,
@@ -263,14 +267,11 @@ export async function lookupExchangeByUri(
 }
 
 /**
- * Mark a ToS version as accepted by the user.
- *
- * @param etag version of the ToS to accept, or current ToS version of not 
given
+ * Mark the current ToS version as accepted by the user.
  */
 export async function acceptExchangeTermsOfService(
   ws: InternalWalletState,
   exchangeBaseUrl: string,
-  etag: string | undefined,
 ): Promise<void> {
   const notif = await ws.db
     .mktx((x) => [x.exchanges, x.exchangeDetails])
@@ -298,6 +299,11 @@ export async function acceptExchangeTermsOfService(
   }
 }
 
+/**
+ * Validate wire fees and wire accounts.
+ *
+ * Throw an exception if they are invalid.
+ */
 async function validateWireInfo(
   ws: InternalWalletState,
   versionCurrent: number,
@@ -364,6 +370,11 @@ async function validateWireInfo(
   };
 }
 
+/**
+ * Validate global fees.
+ *
+ * Throw an exception if they are invalid.
+ */
 async function validateGlobalFees(
   ws: InternalWalletState,
   fees: GlobalFees[],
@@ -455,7 +466,6 @@ async function provideExchangeRecordInTx(
     exchangeDetails: typeof WalletStoresV1.exchangeDetails;
   }>,
   baseUrl: string,
-  now: AbsoluteTime,
 ): Promise<{
   exchange: ExchangeEntryRecord;
   exchangeDetails: ExchangeDetailsRecord | undefined;
@@ -655,7 +665,7 @@ async function downloadExchangeKeysInfo(
     reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay,
     expiry: AbsoluteTime.toProtocolTimestamp(
       getExpiry(resp, {
-        minDuration: durationFromSpec({ hours: 1 }),
+        minDuration: Duration.fromSpec({ hours: 1 }),
       }),
     ),
     recoup: exchangeKeysJsonUnchecked.recoup ?? [],
@@ -718,12 +728,10 @@ export async function startUpdateExchangeEntry(
 ): Promise<void> {
   const canonBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl);
 
-  const now = AbsoluteTime.now();
-
   const { notification } = await ws.db
     .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadWrite(async (tx) => {
-      return provideExchangeRecordInTx(ws, tx, exchangeBaseUrl, now);
+      return provideExchangeRecordInTx(ws, tx, exchangeBaseUrl);
     });
 
   if (notification) {
@@ -778,46 +786,6 @@ export async function startUpdateExchangeEntry(
   ws.workAvailable.trigger();
 }
 
-export interface NotificationWaiter {
-  waitNext(): Promise<void>;
-  cancel(): void;
-}
-
-export function createNotificationWaiter(
-  ws: InternalWalletState,
-  pred: (x: WalletNotification) => boolean,
-): NotificationWaiter {
-  ws.ensureTaskLoopRunning();
-  let cancelFn: CancelFn | undefined = undefined;
-  let p: OpenedPromise<void> | undefined = undefined;
-
-  return {
-    cancel() {
-      cancelFn?.();
-    },
-    waitNext(): Promise<void> {
-      if (!p) {
-        p = openPromise();
-        cancelFn = ws.addNotificationListener((notif) => {
-          if (pred(notif)) {
-            // We got a notification that matches our predicate.
-            // Resolve promise for existing waiters,
-            // and create a new promise to wait for the next
-            // notification occurrence.
-            const myResolve = p?.resolve;
-            const myCancel = cancelFn;
-            p = undefined;
-            cancelFn = undefined;
-            myResolve?.();
-            myCancel?.();
-          }
-        });
-      }
-      return p.promise;
-    },
-  };
-}
-
 /**
  * Basic information about an exchange in a ready state.
  */
@@ -1363,6 +1331,9 @@ export async function listExchanges(
  * Transition an exchange to the "used" entry state if necessary.
  *
  * Should be called whenever the exchange is actively used by the client (for 
withdrawals etc.).
+ *
+ * The caller should emit the returned notification iff the current transaction
+ * succeeded.
  */
 export async function markExchangeUsed(
   ws: InternalWalletState,
@@ -1555,3 +1526,88 @@ export async function getExchangeDetailedInfo(
     },
   };
 }
+
+async function internalGetExchangeResources(
+  ws: InternalWalletState,
+  tx: DbReadOnlyTransactionArr<
+    typeof WalletStoresV1,
+    ["exchanges", "coins", "withdrawalGroups"]
+  >,
+  exchangeBaseUrl: string,
+): Promise<GetExchangeResourcesResponse> {
+  let numWithdrawals = 0;
+  let numCoins = 0;
+  numCoins = await tx.coins.indexes.byBaseUrl.count(exchangeBaseUrl);
+  numWithdrawals =
+    await tx.withdrawalGroups.indexes.byExchangeBaseUrl.count(exchangeBaseUrl);
+  const total = numWithdrawals + numCoins;
+  return {
+    hasResources: total != 0,
+  };
+}
+
+export async function deleteExchange(
+  ws: InternalWalletState,
+  req: DeleteExchangeRequest,
+): Promise<void> {
+  let inUse: boolean = false;
+  const exchangeBaseUrl = canonicalizeBaseUrl(req.exchangeBaseUrl);
+  await ws.db.runReadWriteTx(
+    ["exchanges", "coins", "withdrawalGroups", "exchangeDetails"],
+    async (tx) => {
+      const exchangeRec = await tx.exchanges.get(exchangeBaseUrl);
+      if (!exchangeRec) {
+        // Nothing to delete!
+        logger.info("no exchange found to delete");
+        return;
+      }
+      const res = await internalGetExchangeResources(ws, tx, exchangeBaseUrl);
+      if (res.hasResources) {
+        if (req.purge) {
+          const detRecs =
+            await tx.exchangeDetails.indexes.byExchangeBaseUrl.getAll();
+          for (const r of detRecs) {
+            if (r.rowId == null) {
+              // Should never happen, as rowId is the primary key.
+              continue;
+            }
+            await tx.exchangeDetails.delete(r.rowId);
+          }
+          // FIXME: Also remove records related to transactions?
+        } else {
+          inUse = true;
+          return;
+        }
+      }
+      await tx.exchanges.delete(exchangeBaseUrl);
+    },
+  );
+
+  if (inUse) {
+    throw TalerError.fromUncheckedDetail({
+      code: TalerErrorCode.WALLET_EXCHANGE_ENTRY_USED,
+      hint: "Exchange in use.",
+    });
+  }
+}
+
+export async function getExchangeResources(
+  ws: InternalWalletState,
+  exchangeBaseUrl: string,
+): Promise<GetExchangeResourcesResponse> {
+  // Withdrawals include internal withdrawals from peer transactions
+  const res = await ws.db.runReadOnlyTx(
+    ["exchanges", "withdrawalGroups", "coins"],
+    async (tx) => {
+      const exchangeRecord = await tx.exchanges.get(exchangeBaseUrl);
+      if (!exchangeRecord) {
+        return undefined;
+      }
+      return internalGetExchangeResources(ws, tx, exchangeBaseUrl);
+    },
+  );
+  if (!res) {
+    throw Error("exchange not found");
+  }
+  return res;
+}
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index 17b9b407c..59c6ea2f5 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -344,6 +344,7 @@ interface IndexReadOnlyAccessor<RecordType> {
     query?: IDBKeyRange | IDBValidKey,
     count?: number,
   ): Promise<RecordType[]>;
+  count(query?: IDBValidKey): Promise<number>;
 }
 
 type GetIndexReadOnlyAccess<RecordType, IndexMap> = {
@@ -357,6 +358,7 @@ interface IndexReadWriteAccessor<RecordType> {
     query?: IDBKeyRange | IDBValidKey,
     count?: number,
   ): Promise<RecordType[]>;
+  count(query?: IDBValidKey): Promise<number>;
 }
 
 type GetIndexReadWriteAccess<RecordType, IndexMap> = {
@@ -696,6 +698,10 @@ function makeReadContext(
             .getAll(query, count);
           return requestToPromise(req);
         },
+        count(query) {
+          const req = tx.objectStore(storeName).index(indexName).count(query);
+          return requestToPromise(req);
+        },
       };
     }
     ctx[storeAlias] = {
@@ -745,6 +751,10 @@ function makeWriteContext(
             .getAll(query, count);
           return requestToPromise(req);
         },
+        count(query) {
+          const req = tx.objectStore(storeName).index(indexName).count(query);
+          return requestToPromise(req);
+        },
       };
     }
     ctx[storeAlias] = {
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts 
b/packages/taler-wallet-core/src/wallet-api-types.ts
index 7d3dc86a3..2cbf693f5 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -130,6 +130,9 @@ import {
   WithdrawUriInfoResponse,
   WithdrawalTransactionByURIRequest,
   TransactionWithdrawal,
+  GetExchangeResourcesRequest,
+  DeleteExchangeRequest,
+  GetExchangeResourcesResponse,
 } from "@gnu-taler/taler-util";
 import {
   AddBackupProviderRequest,
@@ -238,6 +241,8 @@ export enum WalletApiOperation {
   ListExchangesForScopedCurrency = "listExchangesForScopedCurrency",
   PrepareWithdrawExchange = "prepareWithdrawExchange",
   TestingInfiniteTransactionLoop = "testingInfiniteTransactionLoop",
+  GetExchangeResources = "getExchangeResources",
+  DeleteExchange = "deleteExchange",
 }
 
 // group: Initialization
@@ -667,6 +672,24 @@ export type GetExchangeEntryByUrlOp = {
   response: GetExchangeEntryByUrlResponse;
 };
 
+/**
+ * Get resources associated with an exchange.
+ */
+export type GetExchangeResourcesOp = {
+  op: WalletApiOperation.GetExchangeResources;
+  request: GetExchangeResourcesRequest;
+  response: GetExchangeResourcesResponse;
+};
+
+/**
+ * Get resources associated with an exchange.
+ */
+export type DeleteExchangeOp = {
+  op: WalletApiOperation.GetExchangeResources;
+  request: DeleteExchangeRequest;
+  response: EmptyObject;
+};
+
 /**
  * List currencies known to the wallet.
  */
@@ -1206,6 +1229,8 @@ export type WalletOperations = {
   [WalletApiOperation.UpdateExchangeEntry]: UpdateExchangeEntryOp;
   [WalletApiOperation.PrepareWithdrawExchange]: PrepareWithdrawExchangeOp;
   [WalletApiOperation.TestingInfiniteTransactionLoop]: any;
+  [WalletApiOperation.DeleteExchange]: DeleteExchangeOp;
+  [WalletApiOperation.GetExchangeResources]: GetExchangeResourcesOp;
 };
 
 export type WalletCoreRequestType<
@@ -1229,10 +1254,10 @@ type Primitives = string | number | boolean;
 
 type RecursivePartial<T extends object> = {
   [P in keyof T]?: T[P] extends Array<infer U extends object>
-  ? Array<RecursivePartial<U>>
-  : T[P] extends Array<infer J extends Primitives>
-  ? Array<J>
-  : T[P] extends object
-  ? RecursivePartial<T[P]>
-  : T[P];
+    ? Array<RecursivePartial<U>>
+    : T[P] extends Array<infer J extends Primitives>
+      ? Array<J>
+      : T[P] extends object
+        ? RecursivePartial<T[P]>
+        : T[P];
 } & object;
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 1a876b2c8..3294e2a09 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -73,6 +73,7 @@ import {
   codecForConfirmPeerPushPaymentRequest,
   codecForConvertAmountRequest,
   codecForCreateDepositGroupRequest,
+  codecForDeleteExchangeRequest,
   codecForDeleteStoredBackupRequest,
   codecForDeleteTransactionRequest,
   codecForFailTransactionRequest,
@@ -83,6 +84,7 @@ import {
   codecForGetContractTermsDetails,
   codecForGetCurrencyInfoRequest,
   codecForGetExchangeEntryByUrlRequest,
+  codecForGetExchangeResourcesRequest,
   codecForGetExchangeTosRequest,
   codecForGetWithdrawalDetailsForAmountRequest,
   codecForGetWithdrawalDetailsForUri,
@@ -189,8 +191,10 @@ import {
 import {
   acceptExchangeTermsOfService,
   addPresetExchangeEntry,
+  deleteExchange,
   fetchFreshExchange,
   getExchangeDetailedInfo,
+  getExchangeResources,
   getExchangeTos,
   listExchanges,
   lookupExchangeByUri,
@@ -726,9 +730,9 @@ async function dumpCoins(ws: InternalWalletState): 
Promise<CoinDumpJson> {
           ageCommitmentProof: c.ageCommitmentProof,
           spend_allocation: c.spendAllocation
             ? {
-              amount: c.spendAllocation.amount,
-              id: c.spendAllocation.id,
-            }
+                amount: c.spendAllocation.amount,
+                id: c.spendAllocation.id,
+              }
             : undefined,
         });
       }
@@ -1071,7 +1075,7 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
     }
     case WalletApiOperation.SetExchangeTosAccepted: {
       const req = codecForAcceptExchangeTosRequest().decode(payload);
-      await acceptExchangeTermsOfService(ws, req.exchangeBaseUrl, req.etag);
+      await acceptExchangeTermsOfService(ws, req.exchangeBaseUrl);
       return {};
     }
     case WalletApiOperation.AcceptBankIntegratedWithdrawal: {
@@ -1400,6 +1404,15 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
       ws.workAvailable.trigger();
       return {};
     }
+    case WalletApiOperation.DeleteExchange: {
+      const req = codecForDeleteExchangeRequest().decode(payload);
+      await deleteExchange(ws, req);
+      return {};
+    }
+    case WalletApiOperation.GetExchangeResources: {
+      const req = codecForGetExchangeResourcesRequest().decode(payload);
+      return await getExchangeResources(ws, req.exchangeBaseUrl);
+    }
     case WalletApiOperation.TestingInfiniteTransactionLoop: {
       const myDelayMs = (payload as any).delayMs ?? 5;
       const shouldFetch = !!(payload as any).shouldFetch;
@@ -1616,7 +1629,6 @@ class InternalWalletStateImpl implements 
InternalWalletState {
     createRecoupGroup,
   };
 
-
   refreshOps: RefreshOperations = {
     createRefreshGroup,
   };

-- 
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]