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 tipping stat


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: fix tipping state machine issues
Date: Fri, 02 Jun 2023 15:53:51 +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 1ee9ef80b wallet-core: fix tipping state machine issues
1ee9ef80b is described below

commit 1ee9ef80bddcc91a8e542ffc44cd23e056e751d4
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri Jun 2 15:53:46 2023 +0200

    wallet-core: fix tipping state machine issues
---
 packages/taler-wallet-core/src/db.ts               |  4 +-
 .../src/operations/pay-merchant.ts                 | 29 ++++---
 packages/taler-wallet-core/src/operations/tip.ts   | 96 ++++++++++++++--------
 3 files changed, 82 insertions(+), 47 deletions(-)

diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 74332de33..afc7e2bf8 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -876,7 +876,9 @@ export interface TipRecord {
 export enum TipRecordStatus {
   PendingPickup = 10,
 
-  SuspendidPickup = 21,
+  SuspendidPickup = 20,
+
+  DialogAccept = 30,
 
   Done = 50,
   Aborted = 51,
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index dce2a30ed..0097f5bcc 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -532,21 +532,23 @@ async function processDownloadProposal(
         h: contractTermsHash,
         contractTermsRaw: proposalResp.contract_terms,
       });
-      if (
+      const isResourceFulfillmentUrl =
         fulfillmentUrl &&
         (fulfillmentUrl.startsWith("http://";) ||
-          fulfillmentUrl.startsWith("https://";))
-      ) {
-        const differentPurchase =
-          await tx.purchases.indexes.byFulfillmentUrl.get(fulfillmentUrl);
-        // FIXME: Adjust this to account for refunds, don't count as repurchase
-        // if original order is refunded.
-        if (differentPurchase) {
-          logger.warn("repurchase detected");
-          p.purchaseStatus = PurchaseStatus.RepurchaseDetected;
-          p.repurchaseProposalId = differentPurchase.proposalId;
-          await tx.purchases.put(p);
-        }
+          fulfillmentUrl.startsWith("https://";));
+      let otherPurchase: PurchaseRecord | undefined;
+      if (isResourceFulfillmentUrl) {
+        otherPurchase = await tx.purchases.indexes.byFulfillmentUrl.get(
+          fulfillmentUrl,
+        );
+      }
+      // FIXME: Adjust this to account for refunds, don't count as repurchase
+      // if original order is refunded.
+      if (otherPurchase) {
+        logger.warn("repurchase detected");
+        p.purchaseStatus = PurchaseStatus.RepurchaseDetected;
+        p.repurchaseProposalId = otherPurchase.proposalId;
+        await tx.purchases.put(p);
       } else {
         p.purchaseStatus = PurchaseStatus.DialogProposed;
         await tx.purchases.put(p);
@@ -602,6 +604,7 @@ async function createPurchase(
     (!noncePriv || oldProposal.noncePriv === noncePriv) &&
     oldProposal.claimToken === claimToken
   ) {
+    // FIXME: This lacks proper error handling
     await processDownloadProposal(ws, oldProposal.proposalId);
     return oldProposal.proposalId;
   }
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index 02c933cba..1a565e02f 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -34,7 +34,6 @@ import {
   PrepareTipResult,
   TalerErrorCode,
   TalerPreciseTimestamp,
-  TalerProtocolTimestamp,
   TipPlanchetDetail,
   TransactionAction,
   TransactionMajorState,
@@ -102,17 +101,21 @@ export function computeTipTransactionStatus(
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.Pickup,
       };
+    case TipRecordStatus.DialogAccept:
+      return {
+        major: TransactionMajorState.Dialog,
+        minor: TransactionMinorState.Proposed,
+      };
     case TipRecordStatus.SuspendidPickup:
       return {
         major: TransactionMajorState.Pending,
-        minor: TransactionMinorState.User,
+        minor: TransactionMinorState.Pickup,
       };
     default:
       assertUnreachable(tipRecord.status);
   }
 }
 
-
 export function computeTipTransactionActions(
   tipRecord: TipRecord,
 ): TransactionAction[] {
@@ -125,6 +128,8 @@ export function computeTipTransactionActions(
       return [TransactionAction.Suspend, TransactionAction.Fail];
     case TipRecordStatus.SuspendidPickup:
       return [TransactionAction.Resume, TransactionAction.Fail];
+    case TipRecordStatus.DialogAccept:
+      return [TransactionAction.Abort];
     default:
       assertUnreachable(tipRecord.status);
   }
@@ -190,7 +195,7 @@ export async function prepareTip(
     const newTipRecord: TipRecord = {
       walletTipId: walletTipId,
       acceptedTimestamp: undefined,
-      status: TipRecordStatus.PendingPickup,
+      status: TipRecordStatus.DialogAccept,
       tipAmountRaw: Amounts.stringify(amount),
       tipExpiration: tipPickupStatus.expiration,
       exchangeBaseUrl: tipPickupStatus.exchange_url,
@@ -234,7 +239,6 @@ export async function prepareTip(
 export async function processTip(
   ws: InternalWalletState,
   walletTipId: string,
-  options: Record<string, never> = {},
 ): Promise<OperationAttemptResult> {
   const tipRecord = await ws.db
     .mktx((x) => [x.tips])
@@ -248,14 +252,22 @@ export async function processTip(
     };
   }
 
-  if (tipRecord.pickedUpTimestamp) {
-    logger.warn("tip already picked up");
-    return {
-      type: OperationAttemptResultType.Finished,
-      result: undefined,
-    };
+  switch (tipRecord.status) {
+    case TipRecordStatus.Aborted:
+    case TipRecordStatus.DialogAccept:
+    case TipRecordStatus.Done:
+    case TipRecordStatus.SuspendidPickup:
+      return {
+        type: OperationAttemptResultType.Finished,
+        result: undefined,
+      };
   }
 
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Tip,
+    walletTipId,
+  });
+
   const denomsForWithdraw = tipRecord.denomsSel;
 
   const planchets: DerivedTipPlanchet[] = [];
@@ -391,22 +403,27 @@ export async function processTip(
     });
   }
 
-  await ws.db
+  const transitionInfo = await ws.db
     .mktx((x) => [x.coins, x.coinAvailability, x.denominations, x.tips])
     .runReadWrite(async (tx) => {
       const tr = await tx.tips.get(walletTipId);
       if (!tr) {
         return;
       }
-      if (tr.pickedUpTimestamp) {
+      if (tr.status !== TipRecordStatus.PendingPickup) {
         return;
       }
+      const oldTxState = computeTipTransactionStatus(tr);
       tr.pickedUpTimestamp = TalerPreciseTimestamp.now();
+      tr.status = TipRecordStatus.Done;
       await tx.tips.put(tr);
+      const newTxState = computeTipTransactionStatus(tr);
       for (const cr of newCoinRecords) {
         await makeCoinAvailable(ws, tx, cr);
       }
+      return { oldTxState, newTxState };
     });
+  notifyTransition(ws, transactionId, transitionInfo);
 
   return {
     type: OperationAttemptResultType.Finished,
@@ -416,33 +433,46 @@ export async function processTip(
 
 export async function acceptTip(
   ws: InternalWalletState,
-  tipId: string,
+  walletTipId: string,
 ): Promise<AcceptTipResponse> {
-  const found = await ws.db
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Tip,
+    walletTipId,
+  });
+  const dbRes = await ws.db
     .mktx((x) => [x.tips])
     .runReadWrite(async (tx) => {
-      const tipRecord = await tx.tips.get(tipId);
+      const tipRecord = await tx.tips.get(walletTipId);
       if (!tipRecord) {
         logger.error("tip not found");
-        return undefined;
+        return;
+      }
+      if (tipRecord.status != TipRecordStatus.DialogAccept) {
+        logger.warn("Unable to accept tip in the current state");
+        return { tipRecord };
       }
+      const oldTxState = computeTipTransactionStatus(tipRecord);
       tipRecord.acceptedTimestamp = TalerPreciseTimestamp.now();
+      tipRecord.status = TipRecordStatus.PendingPickup;
       await tx.tips.put(tipRecord);
-
-      return tipRecord;
+      const newTxState = computeTipTransactionStatus(tipRecord);
+      return { tipRecord, transitionInfo: { oldTxState, newTxState } };
     });
 
-  if (found) {
-    await processTip(ws, tipId);
+  if (!dbRes) {
+    throw Error("tip not found");
   }
-  //FIXME: if tip is not found the behavior of the function is the same
-  // as the tip was found and finished
+
+  notifyTransition(ws, transactionId, dbRes.transitionInfo);
+
+  const tipRecord = dbRes.tipRecord;
+
   return {
     transactionId: constructTransactionIdentifier({
       tag: TransactionType.Tip,
-      walletTipId: tipId,
+      walletTipId: walletTipId,
     }),
-    next_url: found?.next_url,
+    next_url: tipRecord.next_url,
   };
 }
 
@@ -472,10 +502,12 @@ export async function suspendTipTransaction(
         case TipRecordStatus.Done:
         case TipRecordStatus.SuspendidPickup:
         case TipRecordStatus.Aborted:
+        case TipRecordStatus.DialogAccept:
           break;
         case TipRecordStatus.PendingPickup:
           newStatus = TipRecordStatus.SuspendidPickup;
           break;
+
         default:
           assertUnreachable(tipRec.status);
       }
@@ -519,14 +551,13 @@ export async function resumeTipTransaction(
       let newStatus: TipRecordStatus | undefined = undefined;
       switch (tipRec.status) {
         case TipRecordStatus.Done:
+        case TipRecordStatus.PendingPickup:
+        case TipRecordStatus.Aborted:
+        case TipRecordStatus.DialogAccept:
           break;
         case TipRecordStatus.SuspendidPickup:
           newStatus = TipRecordStatus.PendingPickup;
           break;
-        case TipRecordStatus.PendingPickup:
-          break;
-        case TipRecordStatus.Aborted:
-          break;
         default:
           assertUnreachable(tipRec.status);
       }
@@ -577,14 +608,13 @@ export async function abortTipTransaction(
       let newStatus: TipRecordStatus | undefined = undefined;
       switch (tipRec.status) {
         case TipRecordStatus.Done:
+        case TipRecordStatus.Aborted:
+        case TipRecordStatus.PendingPickup:
+        case TipRecordStatus.DialogAccept:
           break;
         case TipRecordStatus.SuspendidPickup:
           newStatus = TipRecordStatus.Aborted;
           break;
-        case TipRecordStatus.PendingPickup:
-          break;
-        case TipRecordStatus.Aborted:
-          break;
         default:
           assertUnreachable(tipRec.status);
       }

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