gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: report manual withdrawals pro


From: gnunet
Subject: [taler-wallet-core] branch master updated: report manual withdrawals properly in transaction list
Date: Thu, 16 Jul 2020 11:15:10 +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 75c5c593 report manual withdrawals properly in transaction list
75c5c593 is described below

commit 75c5c59316a428fbebe2448d9d79a70689565657
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Thu Jul 16 14:44:59 2020 +0530

    report manual withdrawals properly in transaction list
---
 src/operations/reserves.ts     |  31 ++++++------
 src/operations/transactions.ts | 110 ++++++++++++++++++++++++++++++-----------
 src/operations/withdraw.ts     |   4 +-
 src/types/dbTypes.ts           |  27 ++++++++--
 src/types/transactions.ts      |  44 ++++++++++++++---
 src/types/walletTypes.ts       |   2 +-
 src/wallet.ts                  |   5 +-
 src/webex/messages.ts          |   2 +-
 src/webex/pages/withdraw.tsx   |   6 +--
 src/webex/wxApi.ts             |   4 +-
 10 files changed, 167 insertions(+), 68 deletions(-)

diff --git a/src/operations/reserves.ts b/src/operations/reserves.ts
index 2761dfaf..ff20ce9b 100644
--- a/src/operations/reserves.ts
+++ b/src/operations/reserves.ts
@@ -53,7 +53,6 @@ import {
   processWithdrawGroup,
   getBankWithdrawalInfo,
   denomSelectionInfoToState,
-  getWithdrawDenomList,
 } from "./withdraw";
 import {
   guardOperationException,
@@ -106,22 +105,25 @@ export async function createReserve(
   let bankInfo: ReserveBankInfo | undefined;
 
   if (req.bankWithdrawStatusUrl) {
-    const denomSelInfo = await selectWithdrawalDenoms(
-      ws,
-      canonExchange,
-      req.amount,
-    );
-    const denomSel = denomSelectionInfoToState(denomSelInfo);
     bankInfo = {
       statusUrl: req.bankWithdrawStatusUrl,
-      amount: req.amount,
-      bankWithdrawalGroupId: encodeCrock(getRandomBytes(32)),
-      withdrawalStarted: false,
-      denomSel,
     };
   }
 
+  const initialWithdrawalGroupId = encodeCrock(getRandomBytes(32));
+
+  const denomSelInfo = await selectWithdrawalDenoms(
+    ws,
+    canonExchange,
+    req.amount,
+  );
+  const initialDenomSel = denomSelectionInfoToState(denomSelInfo);
+
   const reserveRecord: ReserveRecord = {
+    instructedAmount: req.amount,
+    initialWithdrawalGroupId,
+    initialDenomSel,
+    initialWithdrawalStarted: false,
     timestampCreated: now,
     exchangeBaseUrl: canonExchange,
     reservePriv: keypair.priv,
@@ -750,10 +752,9 @@ async function depleteReserve(
 
       let withdrawalGroupId: string;
 
-      const bankInfo = newReserve.bankInfo;
-      if (bankInfo && !bankInfo.withdrawalStarted) {
-        withdrawalGroupId = bankInfo.bankWithdrawalGroupId;
-        bankInfo.withdrawalStarted = true;
+      if (!newReserve.initialWithdrawalStarted) {
+        withdrawalGroupId = newReserve.initialWithdrawalGroupId;
+        newReserve.initialWithdrawalStarted = true;
       } else {
         withdrawalGroupId = encodeCrock(randomBytes(32));
       }
diff --git a/src/operations/transactions.ts b/src/operations/transactions.ts
index 9a3d48bb..f2845cb1 100644
--- a/src/operations/transactions.ts
+++ b/src/operations/transactions.ts
@@ -32,7 +32,10 @@ import {
   Transaction,
   TransactionType,
   PaymentStatus,
+  WithdrawalType,
+  WithdrawalDetails,
 } from "../types/transactions";
+import { WithdrawalDetailsResponse } from "../types/walletTypes";
 
 /**
  * Create an event ID from the type and the primary key for the event.
@@ -156,6 +159,7 @@ export async function getTransactions(
       Stores.reserveUpdatedEvents,
       Stores.recoupGroups,
     ],
+    // Report withdrawals that are currently in progress.
     async (tx) => {
       tx.iter(Stores.withdrawalGroups).forEachAsync(async (wsr) => {
         if (
@@ -171,34 +175,62 @@ export async function getTransactions(
           return;
         }
 
-        let amountRaw: AmountJson | undefined = undefined;
-
-        if (wsr.source.type === WithdrawalSourceType.Reserve) {
-          const r = await tx.get(Stores.reserves, wsr.source.reservePub);
-          if (r?.bankInfo?.amount) {
-            amountRaw = r.bankInfo.amount;
+        switch (wsr.source.type) {
+          case WithdrawalSourceType.Reserve: {
+            const r = await tx.get(Stores.reserves, wsr.source.reservePub);
+            if (!r) {
+              break;
+            }
+            let amountRaw: AmountJson | undefined = undefined;
+            if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
+              amountRaw = r.instructedAmount;
+            } else {
+              amountRaw = wsr.denomsSel.totalWithdrawCost;
+            }
+            let withdrawalDetails: WithdrawalDetails;
+            if (r.bankInfo) {
+                withdrawalDetails = {
+                  type: WithdrawalType.TalerBankIntegrationApi,
+                  confirmed: true,
+                  bankConfirmationUrl: r.bankInfo.confirmUrl,
+                };
+            } else {
+              const exchange = await tx.get(Stores.exchanges, 
r.exchangeBaseUrl);
+              if (!exchange) {
+                // FIXME: report somehow
+                break;
+              }
+              withdrawalDetails = {
+                type: WithdrawalType.ManualTransfer,
+                reservePublicKey: r.reservePub,
+                exchangePaytoUris: exchange.wireInfo?.accounts.map((x) => 
x.payto_uri) ?? [],
+              };
+            }
+            transactions.push({
+              type: TransactionType.Withdrawal,
+              amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
+              amountRaw: Amounts.stringify(amountRaw),
+              withdrawalDetails,
+              exchangeBaseUrl: wsr.exchangeBaseUrl,
+              pending: !wsr.timestampFinish,
+              timestamp: wsr.timestampStart,
+              transactionId: makeEventId(
+                TransactionType.Withdrawal,
+                wsr.withdrawalGroupId,
+              ),
+            });
           }
+          break;
+          default:
+            // Tips are reported via their own event
+            break;
         }
-        if (!amountRaw) {
-          amountRaw = wsr.denomsSel.totalWithdrawCost;
-        }
-
-        transactions.push({
-          type: TransactionType.Withdrawal,
-          amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
-          amountRaw: Amounts.stringify(amountRaw),
-          confirmed: true,
-          exchangeBaseUrl: wsr.exchangeBaseUrl,
-          pending: !wsr.timestampFinish,
-          timestamp: wsr.timestampStart,
-          transactionId: makeEventId(
-            TransactionType.Withdrawal,
-            wsr.withdrawalGroupId,
-          ),
-        });
       });
 
-      tx.iter(Stores.reserves).forEach((r) => {
+      // Report pending withdrawals based on reserves that
+      // were created, but where the actual withdrawal group has
+      // not started yet.
+      tx.iter(Stores.reserves).forEachAsync(async (r) => {
         if (shouldSkipCurrency(transactionsRequest, r.currency)) {
           return;
         }
@@ -213,23 +245,41 @@ export async function getTransactions(
           default:
             return;
         }
-        if (!r.bankInfo) {
+        if (r.initialWithdrawalStarted) {
           return;
         }
+        let withdrawalDetails: WithdrawalDetails;
+        if (r.bankInfo) {
+          withdrawalDetails = {
+            type: WithdrawalType.TalerBankIntegrationApi,
+            confirmed: false,
+            bankConfirmationUrl: r.bankInfo.confirmUrl,
+          }
+        } else {
+          const exchange = await tx.get(Stores.exchanges, r.exchangeBaseUrl);
+          if (!exchange) {
+            // FIXME: report somehow
+            return;
+          }
+          withdrawalDetails = {
+            type: WithdrawalType.ManualTransfer,
+            reservePublicKey: r.reservePub,
+            exchangePaytoUris: exchange.wireInfo?.accounts.map((x) => 
x.payto_uri) ?? [],
+          };
+        }
         transactions.push({
           type: TransactionType.Withdrawal,
-          confirmed: false,
-          amountRaw: Amounts.stringify(r.bankInfo.amount),
+          amountRaw: Amounts.stringify(r.instructedAmount),
           amountEffective: Amounts.stringify(
-            r.bankInfo.denomSel.totalCoinValue,
+            r.initialDenomSel.totalCoinValue,
           ),
           exchangeBaseUrl: r.exchangeBaseUrl,
           pending: true,
           timestamp: r.timestampCreated,
-          bankConfirmationUrl: r.bankInfo.confirmUrl,
+          withdrawalDetails: withdrawalDetails,
           transactionId: makeEventId(
             TransactionType.Withdrawal,
-            r.bankInfo.bankWithdrawalGroupId,
+            r.initialWithdrawalGroupId,
           ),
         });
       });
diff --git a/src/operations/withdraw.ts b/src/operations/withdraw.ts
index 28474341..fd850f14 100644
--- a/src/operations/withdraw.ts
+++ b/src/operations/withdraw.ts
@@ -32,7 +32,7 @@ import {
 import {
   BankWithdrawDetails,
   ExchangeWithdrawDetails,
-  WithdrawDetails,
+  WithdrawalDetailsResponse,
   OperationError,
 } from "../types/walletTypes";
 import {
@@ -708,7 +708,7 @@ export async function getWithdrawDetailsForUri(
   ws: InternalWalletState,
   talerWithdrawUri: string,
   maybeSelectedExchange?: string,
-): Promise<WithdrawDetails> {
+): Promise<WithdrawalDetailsResponse> {
   const info = await getBankWithdrawalInfo(ws, talerWithdrawUri);
   let rci: ExchangeWithdrawDetails | undefined = undefined;
   if (maybeSelectedExchange) {
diff --git a/src/types/dbTypes.ts b/src/types/dbTypes.ts
index 6693e22a..55f16f40 100644
--- a/src/types/dbTypes.ts
+++ b/src/types/dbTypes.ts
@@ -221,10 +221,6 @@ export interface ReserveHistoryRecord {
 export interface ReserveBankInfo {
   statusUrl: string;
   confirmUrl?: string;
-  amount: AmountJson;
-  bankWithdrawalGroupId: string;
-  withdrawalStarted: boolean;
-  denomSel: DenomSelectionState;
 }
 
 /**
@@ -285,12 +281,28 @@ export interface ReserveRecord {
    */
   exchangeWire: string;
 
+  /**
+   * Amount that was sent by the user to fund the reserve.
+   */
+  instructedAmount: AmountJson;
+
   /**
    * Extra state for when this is a withdrawal involving
    * a Taler-integrated bank.
    */
   bankInfo?: ReserveBankInfo;
 
+  initialWithdrawalGroupId: string;
+
+  /**
+   * Did we start the first withdrawal for this reserve?
+   *
+   * We only report a pending withdrawal for the reserve before
+   * the first withdrawal has started.
+   */
+  initialWithdrawalStarted: boolean;
+  initialDenomSel: DenomSelectionState;
+
   reserveStatus: ReserveRecordStatus;
 
   /**
@@ -1436,6 +1448,13 @@ export interface DenomSelectionState {
   }[];
 }
 
+/**
+ * Group of withdrawal operations that need to be executed.
+ * (Either for a normal withdrawal or from a tip.)
+ * 
+ * The withdrawal group record is only created after we know
+ * the coin selection we want to withdraw.
+ */
 export interface WithdrawalGroupRecord {
   withdrawalGroupId: string;
 
diff --git a/src/types/transactions.ts b/src/types/transactions.ts
index 6ed9a52d..aa618cd4 100644
--- a/src/types/transactions.ts
+++ b/src/types/transactions.ts
@@ -105,18 +105,35 @@ export const enum TransactionType {
   Tip = "tip",
 }
 
-// This should only be used for actual withdrawals
-// and not for tips that have their own transactions type.
-interface TransactionWithdrawal extends TransactionCommon {
-  type: TransactionType.Withdrawal;
+export const enum WithdrawalType {
+  TalerBankIntegrationApi = "taler-bank-integration-api",
+  ManualTransfer = "manual-transfer",
+}
+
+export type WithdrawalDetails =
+  | WithdrawalDetailsForManualTransfer
+  | WithdrawalDetailsForTalerBankIntegrationApi;
+
+interface WithdrawalDetailsForManualTransfer {
+  type: WithdrawalType.ManualTransfer;
 
   /**
-   * Exchange of the withdrawal.
+   * Public key of the reserve that needs to be funded
+   * manually.
+   */
+  reservePublicKey: string;
+
+  /**
+   * Payto URIs that the exchange supports.
    */
-  exchangeBaseUrl?: string;
+  exchangePaytoUris: string[];
+}
+
+interface WithdrawalDetailsForTalerBankIntegrationApi {
+  type: WithdrawalType.TalerBankIntegrationApi;
 
   /**
-   * true if the bank has confirmed the withdrawal, false if not.
+   * Set to true if the bank has confirmed the withdrawal, false if not.
    * An unconfirmed withdrawal usually requires user-input and should be 
highlighted in the UI.
    * See also bankConfirmationUrl below.
    */
@@ -127,6 +144,17 @@ interface TransactionWithdrawal extends TransactionCommon {
    * initiated confirmation.
    */
   bankConfirmationUrl?: string;
+}
+
+// This should only be used for actual withdrawals
+// and not for tips that have their own transactions type.
+interface TransactionWithdrawal extends TransactionCommon {
+  type: TransactionType.Withdrawal;
+
+  /**
+   * Exchange of the withdrawal.
+   */
+  exchangeBaseUrl: string;
 
   /**
    * Amount that got subtracted from the reserve balance.
@@ -137,6 +165,8 @@ interface TransactionWithdrawal extends TransactionCommon {
    * Amount that actually was (or will be) added to the wallet's balance.
    */
   amountEffective: AmountString;
+
+  withdrawalDetails: WithdrawalDetails;
 }
 
 export const enum PaymentStatus {
diff --git a/src/types/walletTypes.ts b/src/types/walletTypes.ts
index 4b6d867a..74f2428d 100644
--- a/src/types/walletTypes.ts
+++ b/src/types/walletTypes.ts
@@ -146,7 +146,7 @@ export interface ExchangeWithdrawDetails {
   walletVersion: string;
 }
 
-export interface WithdrawDetails {
+export interface WithdrawalDetailsResponse {
   bankWithdrawDetails: BankWithdrawDetails;
   exchangeWithdrawDetails: ExchangeWithdrawDetails | undefined;
 }
diff --git a/src/wallet.ts b/src/wallet.ts
index e04c849d..737704fd 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -64,10 +64,9 @@ import {
   TipStatus,
   WalletBalance,
   PreparePayResult,
-  WithdrawDetails,
+  WithdrawalDetailsResponse,
   AcceptWithdrawalResponse,
   PurchaseDetails,
-  ExchangeWithdrawDetails as ExchangeWithdrawalDetails,
   RefreshReason,
   ExchangeListItem,
   ExchangesListRespose,
@@ -477,7 +476,7 @@ export class Wallet {
   async getWithdrawDetailsForUri(
     talerWithdrawUri: string,
     maybeSelectedExchange?: string,
-  ): Promise<WithdrawDetails> {
+  ): Promise<WithdrawalDetailsResponse> {
     return getWithdrawDetailsForUri(
       this.ws,
       talerWithdrawUri,
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 8120d4f9..5cf2fefd 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -146,7 +146,7 @@ export interface MessageMap {
       talerWithdrawUri: string;
       maybeSelectedExchange: string | undefined;
     };
-    response: walletTypes.WithdrawDetails;
+    response: walletTypes.WithdrawalDetailsResponse;
   };
   "accept-withdrawal": {
     request: { talerWithdrawUri: string; selectedExchange: string };
diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx
index d8ac3c45..c4e4ebbb 100644
--- a/src/webex/pages/withdraw.tsx
+++ b/src/webex/pages/withdraw.tsx
@@ -23,7 +23,7 @@
 
 import * as i18n from "../i18n";
 
-import { WithdrawDetails } from "../../types/walletTypes";
+import { WithdrawalDetailsResponse } from "../../types/walletTypes";
 
 import { WithdrawDetailView, renderAmount } from "../renderHtml";
 
@@ -35,7 +35,7 @@ import {
 } from "../wxApi";
 
 function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
-  const [details, setDetails] = useState<WithdrawDetails | undefined>();
+  const [details, setDetails] = useState<WithdrawalDetailsResponse | 
undefined>();
   const [selectedExchange, setSelectedExchange] = useState<
     string | undefined
   >();
@@ -56,7 +56,7 @@ function WithdrawalDialog(props: { talerWithdrawUri: string 
}): JSX.Element {
   useEffect(() => {
     const fetchData = async (): Promise<void> => {
       console.log("getting from", talerWithdrawUri);
-      let d: WithdrawDetails | undefined = undefined;
+      let d: WithdrawalDetailsResponse | undefined = undefined;
       try {
         d = await getWithdrawDetails(talerWithdrawUri, selectedExchange);
       } catch (e) {
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 0901005b..47e73ca4 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -38,7 +38,7 @@ import {
   WalletBalance,
   PurchaseDetails,
   WalletDiagnostics,
-  WithdrawDetails,
+  WithdrawalDetailsResponse,
   PreparePayResult,
   AcceptWithdrawalResponse,
   ExtendedPermissionsResponse,
@@ -283,7 +283,7 @@ export function benchmarkCrypto(repetitions: number): 
Promise<BenchmarkResult> {
 export function getWithdrawDetails(
   talerWithdrawUri: string,
   maybeSelectedExchange: string | undefined,
-): Promise<WithdrawDetails> {
+): Promise<WithdrawalDetailsResponse> {
   return callBackend("get-withdraw-details", {
     talerWithdrawUri,
     maybeSelectedExchange,

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