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: fix withdrawal i


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: fix withdrawal idempotency
Date: Wed, 24 Aug 2022 19:44:27 +0200

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 42c2b750 wallet-core: fix withdrawal idempotency
42c2b750 is described below

commit 42c2b7508f898ccce4305d56084ced971d99ed52
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Aug 24 19:44:24 2022 +0200

    wallet-core: fix withdrawal idempotency
---
 packages/taler-util/src/walletTypes.ts             |  1 -
 .../test-withdrawal-bank-integrated.ts             | 12 +++-
 packages/taler-wallet-core/src/db.ts               | 10 +--
 packages/taler-wallet-core/src/dbless.ts           | 11 +++-
 .../taler-wallet-core/src/operations/withdraw.ts   | 77 +++++++++++++++-------
 packages/taler-wallet-core/src/wallet.ts           |  5 +-
 6 files changed, 78 insertions(+), 38 deletions(-)

diff --git a/packages/taler-util/src/walletTypes.ts 
b/packages/taler-util/src/walletTypes.ts
index 3a415b22..6d3837a6 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -452,7 +452,6 @@ export interface BankWithdrawDetails {
   suggestedExchange?: string;
   confirmTransferUrl?: string;
   wireTypes: string[];
-  extractedStatusUrl: string;
 }
 
 export interface AcceptWithdrawalResponse {
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
 
b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
index e8a8c502..dc7298e5 100644
--- 
a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ 
b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -24,6 +24,7 @@ import {
   BankApi,
   BankAccessApi,
 } from "@gnu-taler/taler-wallet-core";
+import { j2s } from "@gnu-taler/taler-util";
 
 /**
  * Run test for basic, bank-integrated withdrawal.
@@ -62,6 +63,14 @@ export async function runWithdrawalBankIntegratedTest(t: 
GlobalTestState) {
       talerWithdrawUri: wop.taler_withdraw_uri,
     },
   );
+  // Do it twice to check idempotency
+  const r3 = await wallet.client.call(
+    WalletApiOperation.AcceptBankIntegratedWithdrawal,
+    {
+      exchangeBaseUrl: exchange.baseUrl,
+      talerWithdrawUri: wop.taler_withdraw_uri,
+    },
+  );
   await wallet.runPending();
 
   // Confirm it
@@ -75,7 +84,8 @@ export async function runWithdrawalBankIntegratedTest(t: 
GlobalTestState) {
   const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
   t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
 
-  await t.shutdown();
+  const txn = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+  console.log(`transactions: ${j2s(txn)}`);
 }
 
 runWithdrawalBankIntegratedTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index bc0bb4f6..3d59ce0a 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -112,11 +112,7 @@ export enum ReserveRecordStatus {
  * with a bank-integrated withdrawal.
  */
 export interface ReserveBankInfo {
-  /**
-   * Status URL that the wallet will use to query the status
-   * of the Taler withdrawal operation on the bank's side.
-   */
-  statusUrl: string;
+  talerWithdrawUri: string;
 
   /**
    * URL that the user can be redirected to, and allows
@@ -1799,6 +1795,10 @@ export const WalletStoresV1 = {
     {
       byReservePub: describeIndex("byReservePub", "reservePub"),
       byStatus: describeIndex("byStatus", "operationStatus"),
+      byTalerWithdrawUri: describeIndex(
+        "byTalerWithdrawUri",
+        "bankInfo.talerWithdrawUri",
+      ),
     },
   ),
   planchets: describeStore(
diff --git a/packages/taler-wallet-core/src/dbless.ts 
b/packages/taler-wallet-core/src/dbless.ts
index 9bc9c36b..4669b0be 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -46,6 +46,8 @@ import {
   parsePaytoUri,
   AbsoluteTime,
   UnblindedSignature,
+  BankWithdrawDetails,
+  parseWithdrawUri,
 } from "@gnu-taler/taler-util";
 import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
 import { DenominationRecord } from "./db.js";
@@ -57,7 +59,12 @@ import {
   isWithdrawableDenom,
   readSuccessResponseJsonOrThrow,
 } from "./index.browser.js";
-import { BankAccessApi, BankApi, BankServiceHandle } from "./index.js";
+import {
+  BankAccessApi,
+  BankApi,
+  BankServiceHandle,
+  getBankStatusUrl,
+} from "./index.js";
 
 const logger = new Logger("dbless.ts");
 
@@ -119,7 +126,7 @@ export async function topupReserveWithDemobank(
     amount,
   );
   const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri);
-  const bankStatusUrl = bankInfo.extractedStatusUrl;
+  const bankStatusUrl = getBankStatusUrl(wopi.taler_withdraw_uri);
   if (!bankInfo.suggestedExchange) {
     throw Error("no suggested exchange");
   }
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 721a043d..3c4e2d98 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -36,7 +36,8 @@ import {
   codecForWithdrawResponse,
   DenomKeyType,
   Duration,
-  durationFromSpec, encodeCrock,
+  durationFromSpec,
+  encodeCrock,
   ExchangeListItem,
   ExchangeWithdrawRequest,
   ForcedDenomSel,
@@ -54,7 +55,8 @@ import {
   VersionMatchResult,
   WithdrawBatchResponse,
   WithdrawResponse,
-  WithdrawUriInfoResponse
+  WithdrawUriInfoResponse,
+  WithdrawUriResult,
 } from "@gnu-taler/taler-util";
 import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
 import {
@@ -71,12 +73,12 @@ import {
   ReserveBankInfo,
   ReserveRecordStatus,
   WalletStoresV1,
-  WithdrawalGroupRecord
+  WithdrawalGroupRecord,
 } from "../db.js";
 import {
   getErrorDetailFromException,
   makeErrorDetail,
-  TalerError
+  TalerError,
 } from "../errors.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
@@ -85,24 +87,21 @@ import {
   HttpRequestLibrary,
   readSuccessResponseJsonOrErrorCode,
   readSuccessResponseJsonOrThrow,
-  throwUnexpectedRequestError
+  throwUnexpectedRequestError,
 } from "../util/http.js";
 import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
-import {
-  DbAccess,
-  GetReadOnlyAccess
-} from "../util/query.js";
+import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
 import { RetryInfo } from "../util/retries.js";
 import {
   WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
-  WALLET_EXCHANGE_PROTOCOL_VERSION
+  WALLET_EXCHANGE_PROTOCOL_VERSION,
 } from "../versions.js";
 import { guardOperationException } from "./common.js";
 import {
   getExchangeDetails,
   getExchangePaytoUri,
   getExchangeTrust,
-  updateExchangeFromUrl
+  updateExchangeFromUrl,
 } from "./exchanges.js";
 
 /**
@@ -241,7 +240,7 @@ export function selectWithdrawalDenominations(
   for (const d of denoms) {
     let count = 0;
     const cost = Amounts.add(d.value, d.feeWithdraw).amount;
-    for (; ;) {
+    for (;;) {
       if (Amounts.cmp(remaining, cost) < 0) {
         break;
       }
@@ -384,7 +383,6 @@ export async function getBankWithdrawalInfo(
   return {
     amount: Amounts.parseOrThrow(status.amount),
     confirmTransferUrl: status.confirm_transfer_url,
-    extractedStatusUrl: reqUrl.href,
     selectionDone: status.selection_done,
     senderWire: status.sender_wire,
     suggestedExchange: status.suggested_exchange,
@@ -898,7 +896,8 @@ export async function updateWithdrawalDenoms(
         denom.verificationStatus === DenominationVerificationStatus.Unverified
       ) {
         logger.trace(
-          `Validating denomination (${current + 1}/${denominations.length
+          `Validating denomination (${current + 1}/${
+            denominations.length
           }) signature of ${denom.denomPubHash}`,
         );
         let valid = false;
@@ -1025,7 +1024,7 @@ async function queryReserve(
     if (
       resp.status === 404 &&
       result.talerErrorResponse.code ===
-      TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
+        TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
     ) {
       ws.notify({
         type: NotificationType.ReserveNotYetFound,
@@ -1315,7 +1314,7 @@ export async function getExchangeWithdrawalInfo(
     ) {
       logger.warn(
         `wallet's support for exchange protocol version 
${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
-        `(exchange has ${exchangeDetails.protocolVersion}), checking for 
updates`,
+          `(exchange has ${exchangeDetails.protocolVersion}), checking for 
updates`,
       );
     }
   }
@@ -1400,11 +1399,10 @@ export async function getWithdrawalDetailsForUri(
       const exchangeRecords = await tx.exchanges.iter().toArray();
       for (const r of exchangeRecords) {
         const details = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl);
-        const denominations = await tx.denominations.indexes
-          .byExchangeBaseUrl.iter(r.baseUrl).toArray();
+        const denominations = await tx.denominations.indexes.byExchangeBaseUrl
+          .iter(r.baseUrl)
+          .toArray();
         if (details && denominations) {
-
-
           exchanges.push({
             exchangeBaseUrl: details.exchangeBaseUrl,
             currency: details.currency,
@@ -1417,7 +1415,7 @@ export async function getWithdrawalDetailsForUri(
             paytoUris: details.wireInfo.accounts.map((x) => x.payto_uri),
             auditors: details.auditors,
             wireInfo: details.wireInfo,
-            denominations: denominations
+            denominations: denominations,
           });
         }
       }
@@ -1502,6 +1500,18 @@ export function getReserveRequestTimeout(r: 
WithdrawalGroupRecord): Duration {
   );
 }
 
+export function getBankStatusUrl(talerWithdrawUri: string): string {
+  const uriResult = parseWithdrawUri(talerWithdrawUri);
+  if (!uriResult) {
+    throw Error(`can't parse withdrawal URL ${talerWithdrawUri}`);
+  }
+  const url = new URL(
+    `withdrawal-operation/${uriResult.withdrawalOperationId}`,
+    uriResult.bankIntegrationApiBaseUrl,
+  );
+  return url.href;
+}
+
 async function registerReserveWithBank(
   ws: InternalWalletState,
   withdrawalGroupId: string,
@@ -1524,7 +1534,7 @@ async function registerReserveWithBank(
   if (!bankInfo) {
     return;
   }
-  const bankStatusUrl = bankInfo.statusUrl;
+  const bankStatusUrl = getBankStatusUrl(bankInfo.talerWithdrawUri);
   const httpResp = await ws.http.postJson(
     bankStatusUrl,
     {
@@ -1584,10 +1594,12 @@ async function processReserveBankStatus(
     default:
       return;
   }
-  const bankStatusUrl = withdrawalGroup.bankInfo?.statusUrl;
-  if (!bankStatusUrl) {
+  if (!withdrawalGroup.bankInfo) {
     return;
   }
+  const bankStatusUrl = getBankStatusUrl(
+    withdrawalGroup.bankInfo.talerWithdrawUri,
+  );
 
   const statusResp = await ws.http.get(bankStatusUrl, {
     timeout: getReserveRequestTimeout(withdrawalGroup),
@@ -1778,6 +1790,21 @@ export async function acceptWithdrawalFromUri(
     restrictAge?: number;
   },
 ): Promise<AcceptWithdrawalResponse> {
+  const existingWithdrawalGroup = await ws.db
+    .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+    .runReadOnly(async (tx) => {
+      return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get(
+        req.talerWithdrawUri,
+      );
+    });
+
+  if (existingWithdrawalGroup) {
+    return {
+      reservePub: existingWithdrawalGroup.reservePub,
+      confirmTransferUrl: existingWithdrawalGroup.bankInfo?.confirmUrl,
+    };
+  }
+
   await updateExchangeFromUrl(ws, req.selectedExchange);
   const withdrawInfo = await getBankWithdrawalInfo(
     ws.http,
@@ -1796,7 +1823,7 @@ export async function acceptWithdrawalFromUri(
     reserveStatus: ReserveRecordStatus.RegisteringBank,
     bankInfo: {
       exchangePaytoUri,
-      statusUrl: withdrawInfo.extractedStatusUrl,
+      talerWithdrawUri: req.talerWithdrawUri,
       confirmUrl: withdrawInfo.confirmTransferUrl,
     },
   });
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index ac81660d..3c83cea6 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -24,7 +24,6 @@
  */
 import {
   AbsoluteTime,
-  AcceptManualWithdrawalResult,
   AmountJson,
   Amounts,
   BalancesResponse,
@@ -98,7 +97,6 @@ import {
   CoinSourceType,
   exportDb,
   importDb,
-  ReserveRecordStatus,
   WalletStoresV1,
 } from "./db.js";
 import { getErrorDetailFromException, TalerError } from "./errors.js";
@@ -187,9 +185,8 @@ import {
   acceptWithdrawalFromUri,
   createManualWithdrawal,
   getExchangeWithdrawalInfo,
-  getFundingPaytoUrisTx,
   getWithdrawalDetailsForUri,
-  processWithdrawalGroup as processWithdrawalGroup,
+  processWithdrawalGroup,
 } from "./operations/withdraw.js";
 import {
   PendingOperationsResponse,

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