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: handle more p2p


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: handle more p2p abort cases nicely
Date: Mon, 05 Jun 2023 17:58:30 +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 9fca44893 wallet-core: handle more p2p abort cases nicely
9fca44893 is described below

commit 9fca44893a6f7fcee5c828da5fc10e7d76592b5d
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Jun 5 17:58:20 2023 +0200

    wallet-core: handle more p2p abort cases nicely
---
 packages/taler-util/src/http-common.ts             |   2 +-
 packages/taler-util/src/taler-crypto.ts            |   1 +
 packages/taler-util/src/transactions-types.ts      |   1 +
 packages/taler-util/src/wallet-types.ts            |   1 +
 .../src/crypto/cryptoImplementation.ts             |  24 +++
 .../taler-wallet-core/src/crypto/cryptoTypes.ts    |   9 +-
 packages/taler-wallet-core/src/db.ts               |   3 +
 .../src/operations/pay-peer-pull-credit.ts         |  59 ++++++-
 .../src/operations/pay-peer-pull-debit.ts          | 127 ++++++++++++--
 .../src/operations/pay-peer-push-credit.ts         |  70 --------
 .../src/operations/pay-peer-push-debit.ts          | 188 +++++++++++++++++++--
 11 files changed, 385 insertions(+), 100 deletions(-)

diff --git a/packages/taler-util/src/http-common.ts 
b/packages/taler-util/src/http-common.ts
index 4f8f12789..4f6aaaf44 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -45,7 +45,7 @@ export interface HttpResponse {
 export const DEFAULT_REQUEST_TIMEOUT_MS = 60000;
 
 export interface HttpRequestOptions {
-  method?: "POST" | "PUT" | "GET";
+  method?: "POST" | "PUT" | "GET" | "DELETE";
   headers?: { [name: string]: string };
 
   /**
diff --git a/packages/taler-util/src/taler-crypto.ts 
b/packages/taler-util/src/taler-crypto.ts
index 4a657b621..c4eb925f7 100644
--- a/packages/taler-util/src/taler-crypto.ts
+++ b/packages/taler-util/src/taler-crypto.ts
@@ -958,6 +958,7 @@ export enum TalerSignaturePurpose {
   WALLET_PURSE_MERGE = 1213,
   WALLET_ACCOUNT_MERGE = 1214,
   WALLET_PURSE_ECONTRACT = 1216,
+  WALLET_PURSE_DELETE = 1220,
   EXCHANGE_CONFIRM_RECOUP = 1039,
   EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
   ANASTASIS_POLICY_UPLOAD = 1400,
diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index 3f3fe8ac0..576c8e335 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -83,6 +83,7 @@ export enum TransactionMajorState {
   Dialog = "dialog",
   SuspendedAborting = "suspended-aborting",
   Failed = "failed",
+  Expired = "expired",
   // Only used for the notification, never in the transaction history
   Deleted = "deleted",
 }
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index 05ff8d891..af02807a6 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -734,6 +734,7 @@ export enum RefreshReason {
   Refund = "refund",
   AbortPay = "abort-pay",
   AbortDeposit = "abort-deposit",
+  AbortPeerPushDebit = "abort-peer-push-debit",
   Recoup = "recoup",
   BackupRestored = "backup-restored",
   Scheduled = "scheduled",
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 6b44c297d..c0c8e0d01 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -106,6 +106,8 @@ import {
   EncryptContractRequest,
   EncryptContractResponse,
   EncryptedContract,
+  SignDeletePurseRequest,
+  SignDeletePurseResponse,
   SignPurseMergeRequest,
   SignPurseMergeResponse,
   SignRefundRequest,
@@ -240,6 +242,8 @@ export interface TalerCryptoInterface {
   ): Promise<SignReservePurseCreateResponse>;
 
   signRefund(req: SignRefundRequest): Promise<SignRefundResponse>;
+
+  signDeletePurse(req: SignDeletePurseRequest): 
Promise<SignDeletePurseResponse>;
 }
 
 /**
@@ -419,6 +423,11 @@ export const nullCrypto: TalerCryptoInterface = {
   signRefund: function (req: SignRefundRequest): Promise<SignRefundResponse> {
     throw new Error("Function not implemented.");
   },
+  signDeletePurse: function (
+    req: SignDeletePurseRequest,
+  ): Promise<SignDeletePurseResponse> {
+    throw new Error("Function not implemented.");
+  },
 };
 
 export type WithArg<X> = X extends (req: infer T) => infer R
@@ -1671,6 +1680,21 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
       sig: refundSigResp.sig,
     };
   },
+  async signDeletePurse(
+    tci: TalerCryptoInterfaceR,
+    req: SignDeletePurseRequest,
+  ): Promise<SignDeletePurseResponse> {
+    const deleteSigBlob = buildSigPS(
+      TalerSignaturePurpose.WALLET_PURSE_DELETE,
+    ).build();
+    const sigResp = await tci.eddsaSign(tci, {
+      msg: encodeCrock(deleteSigBlob),
+      priv: req.pursePriv,
+    });
+    return {
+      sig: sigResp.sig,
+    }
+  },
 };
 
 function amountToBuffer(amount: AmountLike): Uint8Array {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts 
b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 3b27db0c0..930db03a8 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -268,7 +268,14 @@ export interface SignRefundResponse {
   sig: string;
 }
 
-export interface SignRefundResponse {}
+
+export interface SignDeletePurseRequest {
+  pursePriv: string;
+}
+
+export interface SignDeletePurseResponse {
+  sig: EddsaSignatureString;
+}
 
 export interface SignReservePurseCreateRequest {
   mergeTimestamp: TalerProtocolTimestamp;
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 3f19822c8..d64d1fbc6 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1787,6 +1787,7 @@ export enum PeerPushPaymentInitiationStatus {
   Done = 50 /* DORMANT_START */,
   Aborted = 51,
   Failed = 52,
+  Expired = 53,
 }
 
 export interface PeerPushPaymentCoinSelection {
@@ -1844,6 +1845,8 @@ export interface PeerPushPaymentInitiationRecord {
 
   timestampCreated: TalerPreciseTimestamp;
 
+  abortRefreshGroupId?: string;
+
   /**
    * Status of the peer push payment initiation.
    */
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index 333202a69..e9c34cf73 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -83,7 +83,6 @@ import {
   stopLongpolling,
 } from "./transactions.js";
 import {
-  checkWithdrawalKycStatus,
   getExchangeWithdrawalInfo,
   internalCreateWithdrawalGroup,
   processWithdrawalGroup,
@@ -241,6 +240,62 @@ async function longpollKycStatus(
   };
 }
 
+async function processPeerPullCreditAbortingDeletePurse(
+  ws: InternalWalletState,
+  peerPullIni: PeerPullPaymentInitiationRecord,
+): Promise<OperationAttemptResult> {
+  const { pursePub, pursePriv } = peerPullIni;
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+
+  const sigResp = await ws.cryptoApi.signDeletePurse({
+    pursePriv,
+  });
+  const purseUrl = new URL(
+    `purses/${pursePub}`,
+    peerPullIni.exchangeBaseUrl,
+  );
+  const resp = await ws.http.fetch(purseUrl.href, {
+    method: "DELETE",
+    headers: {
+      "taler-purse-signature": sigResp.sig,
+    },
+  });
+  logger.info(`deleted purse with response status ${resp.status}`);
+
+  const transitionInfo = await ws.db
+    .mktx((x) => [
+      x.peerPullPaymentInitiations,
+      x.refreshGroups,
+      x.denominations,
+      x.coinAvailability,
+      x.coins,
+    ])
+    .runReadWrite(async (tx) => {
+      const ppiRec = await tx.peerPullPaymentInitiations.get(pursePub);
+      if (!ppiRec) {
+        return undefined;
+      }
+      if (
+        ppiRec.status !== PeerPullPaymentInitiationStatus.AbortingDeletePurse
+      ) {
+        return undefined;
+      }
+      const oldTxState = computePeerPullCreditTransactionState(ppiRec);
+      ppiRec.status = PeerPullPaymentInitiationStatus.Aborted;
+      const newTxState = computePeerPullCreditTransactionState(ppiRec);
+      return {
+        oldTxState,
+        newTxState,
+      };
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+
+  return OperationAttemptResult.pendingEmpty();
+}
+
 export async function processPeerPullCredit(
   ws: InternalWalletState,
   pursePub: string,
@@ -320,6 +375,8 @@ export async function processPeerPullCredit(
     }
     case PeerPullPaymentInitiationStatus.PendingCreatePurse:
       break;
+    case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+      return await processPeerPullCreditAbortingDeletePurse(ws, pullIni);
     default:
       throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`);
   }
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
index fdec42bbd..212d69eea 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
@@ -15,55 +15,152 @@
  */
 
 import {
-  ConfirmPeerPullDebitRequest,
   AcceptPeerPullPaymentResponse,
   Amounts,
-  j2s,
-  TalerError,
-  TalerErrorCode,
-  TransactionType,
-  RefreshReason,
+  ConfirmPeerPullDebitRequest,
+  ExchangePurseDeposits,
   Logger,
   PeerContractTerms,
   PreparePeerPullDebitRequest,
   PreparePeerPullDebitResponse,
+  RefreshReason,
+  TalerError,
+  TalerErrorCode,
   TalerPreciseTimestamp,
+  TransactionAction,
+  TransactionMajorState,
+  TransactionMinorState,
+  TransactionState,
+  TransactionType,
+  codecForAny,
   codecForExchangeGetContractResponse,
   codecForPeerContractTerms,
   decodeCrock,
   eddsaGetPublic,
   encodeCrock,
   getRandomBytes,
+  j2s,
   parsePayPullUri,
-  TransactionAction,
-  TransactionMajorState,
-  TransactionMinorState,
-  TransactionState,
 } from "@gnu-taler/taler-util";
+import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
 import {
   InternalWalletState,
   PeerPullDebitRecordStatus,
   PeerPullPaymentIncomingRecord,
   PendingTaskType,
 } from "../index.js";
-import { TaskIdentifiers, constructTaskIdentifier } from "../util/retries.js";
-import { spendCoins, runOperationWithErrorReporting } from "./common.js";
+import { assertUnreachable } from "../util/assertUnreachable.js";
+import {
+  OperationAttemptResult,
+  OperationAttemptResultType,
+  TaskIdentifiers,
+  constructTaskIdentifier,
+} from "../util/retries.js";
+import { runOperationWithErrorReporting, spendCoins } from "./common.js";
 import {
   codecForExchangePurseStatus,
   getTotalPeerPaymentCost,
+  queryCoinInfosForSelection,
   selectPeerCoins,
 } from "./pay-peer-common.js";
-import { processPeerPullDebit } from "./pay-peer-push-credit.js";
 import {
   constructTransactionIdentifier,
   notifyTransition,
   stopLongpolling,
 } from "./transactions.js";
-import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { assertUnreachable } from "../util/assertUnreachable.js";
 
 const logger = new Logger("pay-peer-pull-debit.ts");
 
+async function processPeerPullDebitPendingDeposit(
+  ws: InternalWalletState,
+  peerPullInc: PeerPullPaymentIncomingRecord,
+): Promise<OperationAttemptResult> {
+  const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
+  const pursePub = peerPullInc.pursePub;
+
+  const coinSel = peerPullInc.coinSel;
+  if (!coinSel) {
+    throw Error("invalid state, no coins selected");
+  }
+
+  const coins = await queryCoinInfosForSelection(ws, coinSel);
+
+  const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
+    exchangeBaseUrl: peerPullInc.exchangeBaseUrl,
+    pursePub: peerPullInc.pursePub,
+    coins,
+  });
+
+  const purseDepositUrl = new URL(
+    `purses/${pursePub}/deposit`,
+    peerPullInc.exchangeBaseUrl,
+  );
+
+  const depositPayload: ExchangePurseDeposits = {
+    deposits: depositSigsResp.deposits,
+  };
+
+  if (logger.shouldLogTrace()) {
+    logger.trace(`purse deposit payload: ${j2s(depositPayload)}`);
+  }
+
+  const httpResp = await ws.http.postJson(purseDepositUrl.href, 
depositPayload);
+  const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
+  logger.trace(`purse deposit response: ${j2s(resp)}`);
+
+  await ws.db
+    .mktx((x) => [x.peerPullPaymentIncoming])
+    .runReadWrite(async (tx) => {
+      const pi = await tx.peerPullPaymentIncoming.get(
+        peerPullPaymentIncomingId,
+      );
+      if (!pi) {
+        throw Error("peer pull payment not found anymore");
+      }
+      if (pi.status === PeerPullDebitRecordStatus.PendingDeposit) {
+        pi.status = PeerPullDebitRecordStatus.DonePaid;
+      }
+      await tx.peerPullPaymentIncoming.put(pi);
+    });
+
+  return {
+    type: OperationAttemptResultType.Finished,
+    result: undefined,
+  };
+}
+
+async function processPeerPullDebitAbortingRefresh(
+  ws: InternalWalletState,
+  peerPullInc: PeerPullPaymentIncomingRecord,
+): Promise<OperationAttemptResult> {
+  throw Error("not implemented");
+}
+
+export async function processPeerPullDebit(
+  ws: InternalWalletState,
+  peerPullPaymentIncomingId: string,
+): Promise<OperationAttemptResult> {
+  const peerPullInc = await ws.db
+    .mktx((x) => [x.peerPullPaymentIncoming])
+    .runReadOnly(async (tx) => {
+      return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId);
+    });
+  if (!peerPullInc) {
+    throw Error("peer pull debit not found");
+  }
+
+  switch (peerPullInc.status) {
+    case PeerPullDebitRecordStatus.PendingDeposit:
+      return await processPeerPullDebitPendingDeposit(ws, peerPullInc);
+    case PeerPullDebitRecordStatus.AbortingRefresh:
+      return await processPeerPullDebitAbortingRefresh(ws, peerPullInc);
+  }
+  return {
+    type: OperationAttemptResultType.Finished,
+    result: undefined,
+  }
+}
+
 export async function confirmPeerPullDebit(
   ws: InternalWalletState,
   req: ConfirmPeerPullDebitRequest,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
index 91b0b6022..1a79c7b87 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
@@ -553,76 +553,6 @@ export async function confirmPeerPushCredit(
   };
 }
 
-export async function processPeerPullDebit(
-  ws: InternalWalletState,
-  peerPullPaymentIncomingId: string,
-): Promise<OperationAttemptResult> {
-  const peerPullInc = await ws.db
-    .mktx((x) => [x.peerPullPaymentIncoming])
-    .runReadOnly(async (tx) => {
-      return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId);
-    });
-  if (!peerPullInc) {
-    throw Error("peer pull debit not found");
-  }
-  if (peerPullInc.status === PeerPullDebitRecordStatus.PendingDeposit) {
-    const pursePub = peerPullInc.pursePub;
-
-    const coinSel = peerPullInc.coinSel;
-    if (!coinSel) {
-      throw Error("invalid state, no coins selected");
-    }
-
-    const coins = await queryCoinInfosForSelection(ws, coinSel);
-
-    const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
-      exchangeBaseUrl: peerPullInc.exchangeBaseUrl,
-      pursePub: peerPullInc.pursePub,
-      coins,
-    });
-
-    const purseDepositUrl = new URL(
-      `purses/${pursePub}/deposit`,
-      peerPullInc.exchangeBaseUrl,
-    );
-
-    const depositPayload: ExchangePurseDeposits = {
-      deposits: depositSigsResp.deposits,
-    };
-
-    if (logger.shouldLogTrace()) {
-      logger.trace(`purse deposit payload: ${j2s(depositPayload)}`);
-    }
-
-    const httpResp = await ws.http.postJson(
-      purseDepositUrl.href,
-      depositPayload,
-    );
-    const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
-    logger.trace(`purse deposit response: ${j2s(resp)}`);
-  }
-
-  await ws.db
-    .mktx((x) => [x.peerPullPaymentIncoming])
-    .runReadWrite(async (tx) => {
-      const pi = await tx.peerPullPaymentIncoming.get(
-        peerPullPaymentIncomingId,
-      );
-      if (!pi) {
-        throw Error("peer pull payment not found anymore");
-      }
-      if (pi.status === PeerPullDebitRecordStatus.PendingDeposit) {
-        pi.status = PeerPullDebitRecordStatus.DonePaid;
-      }
-      await tx.peerPullPaymentIncoming.put(pi);
-    });
-
-  return {
-    type: OperationAttemptResultType.Finished,
-    result: undefined,
-  };
-}
-
 export async function suspendPeerPushCreditTransaction(
   ws: InternalWalletState,
   peerPushPaymentIncomingId: string,
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts 
b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index dead6313d..ac0aa9c87 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -18,6 +18,7 @@ import {
   Amounts,
   CheckPeerPushDebitRequest,
   CheckPeerPushDebitResponse,
+  CoinRefreshRequest,
   ContractTermsUtil,
   HttpStatusCode,
   InitiatePeerPushDebitRequest,
@@ -27,13 +28,14 @@ import {
   TalerError,
   TalerErrorCode,
   TalerPreciseTimestamp,
+  TalerUriAction,
   TransactionAction,
   TransactionMajorState,
   TransactionMinorState,
   TransactionState,
   TransactionType,
-  constructPayPushUri,
   j2s,
+  stringifyTalerUri,
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import {
@@ -46,6 +48,8 @@ import { readSuccessResponseJsonOrThrow } from 
"@gnu-taler/taler-util/http";
 import {
   PeerPushPaymentInitiationRecord,
   PeerPushPaymentInitiationStatus,
+  RefreshOperationStatus,
+  createRefreshGroup,
 } from "../index.js";
 import { PendingTaskType } from "../pending-types.js";
 import {
@@ -64,6 +68,7 @@ import {
   stopLongpolling,
 } from "./transactions.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
+import { checkLogicInvariant } from "../util/invariants.js";
 
 const logger = new Logger("pay-peer-push-debit.ts");
 
@@ -172,9 +177,89 @@ async function processPeerPushDebitCreateReserve(
   };
 }
 
-async function transitionPeerPushDebitFromReadyToDone(
+async function processPeerPushDebitAbortingDeletePurse(
+  ws: InternalWalletState,
+  peerPushInitiation: PeerPushPaymentInitiationRecord,
+): Promise<OperationAttemptResult> {
+  const { pursePub, pursePriv } = peerPushInitiation;
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+
+  const sigResp = await ws.cryptoApi.signDeletePurse({
+    pursePriv,
+  });
+  const purseUrl = new URL(
+    `purses/${pursePub}`,
+    peerPushInitiation.exchangeBaseUrl,
+  );
+  const resp = await ws.http.fetch(purseUrl.href, {
+    method: "DELETE",
+    headers: {
+      "taler-purse-signature": sigResp.sig,
+    },
+  });
+  logger.info(`deleted purse with response status ${resp.status}`);
+
+  const transitionInfo = await ws.db
+    .mktx((x) => [
+      x.peerPushPaymentInitiations,
+      x.refreshGroups,
+      x.denominations,
+      x.coinAvailability,
+      x.coins,
+    ])
+    .runReadWrite(async (tx) => {
+      const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!ppiRec) {
+        return undefined;
+      }
+      if (
+        ppiRec.status !== PeerPushPaymentInitiationStatus.AbortingDeletePurse
+      ) {
+        return undefined;
+      }
+      const currency = Amounts.currencyOf(ppiRec.amount);
+      const oldTxState = computePeerPushDebitTransactionState(ppiRec);
+      const coinPubs: CoinRefreshRequest[] = [];
+
+      for (let i = 0; i < ppiRec.coinSel.coinPubs.length; i++) {
+        coinPubs.push({
+          amount: ppiRec.coinSel.contributions[i],
+          coinPub: ppiRec.coinSel.coinPubs[i],
+        });
+      }
+
+      const refresh = await createRefreshGroup(
+        ws,
+        tx,
+        currency,
+        coinPubs,
+        RefreshReason.AbortPeerPushDebit,
+      );
+      ppiRec.status = PeerPushPaymentInitiationStatus.AbortingRefresh;
+      ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
+      const newTxState = computePeerPushDebitTransactionState(ppiRec);
+      return {
+        oldTxState,
+        newTxState,
+      };
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+
+  return OperationAttemptResult.pendingEmpty();
+}
+
+interface SimpleTransition {
+  stFrom: PeerPushPaymentInitiationStatus;
+  stTo: PeerPushPaymentInitiationStatus;
+}
+
+async function transitionPeerPushDebitTransaction(
   ws: InternalWalletState,
   pursePub: string,
+  transitionSpec: SimpleTransition,
 ): Promise<void> {
   const transactionId = constructTransactionIdentifier({
     tag: TransactionType.PeerPushDebit,
@@ -187,11 +272,11 @@ async function transitionPeerPushDebitFromReadyToDone(
       if (!ppiRec) {
         return undefined;
       }
-      if (ppiRec.status !== PeerPushPaymentInitiationStatus.PendingReady) {
+      if (ppiRec.status !== transitionSpec.stFrom) {
         return undefined;
       }
       const oldTxState = computePeerPushDebitTransactionState(ppiRec);
-      ppiRec.status = PeerPushPaymentInitiationStatus.Done;
+      ppiRec.status = transitionSpec.stTo;
       const newTxState = computePeerPushDebitTransactionState(ppiRec);
       return {
         oldTxState,
@@ -201,6 +286,54 @@ async function transitionPeerPushDebitFromReadyToDone(
   notifyTransition(ws, transactionId, transitionInfo);
 }
 
+async function processPeerPushDebitAbortingRefresh(
+  ws: InternalWalletState,
+  peerPushInitiation: PeerPushPaymentInitiationRecord,
+): Promise<OperationAttemptResult> {
+  const pursePub = peerPushInitiation.pursePub;
+  const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
+  checkLogicInvariant(!!abortRefreshGroupId);
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub: peerPushInitiation.pursePub,
+  });
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.refreshGroups, x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
+      let newOpState: PeerPushPaymentInitiationStatus | undefined;
+      if (!refreshGroup) {
+        // Maybe it got manually deleted? Means that we should
+        // just go into failed.
+        logger.warn("no aborting refresh group found for deposit group");
+        newOpState = PeerPushPaymentInitiationStatus.Failed;
+      } else {
+        if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) {
+          newOpState = PeerPushPaymentInitiationStatus.Aborted;
+        } else if (
+          refreshGroup.operationStatus === RefreshOperationStatus.Failed
+        ) {
+          newOpState = PeerPushPaymentInitiationStatus.Failed;
+        }
+      }
+      if (newOpState) {
+        const newDg = await tx.peerPushPaymentInitiations.get(pursePub);
+        if (!newDg) {
+          return;
+        }
+        const oldTxState = computePeerPushDebitTransactionState(newDg);
+        newDg.status = newOpState;
+        const newTxState = computePeerPushDebitTransactionState(newDg);
+        await tx.peerPushPaymentInitiations.put(newDg);
+        return { oldTxState, newTxState };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+  // FIXME: Shouldn't this be finished in some cases?!
+  return OperationAttemptResult.pendingEmpty();
+}
+
 /**
  * Process the "pending(ready)" state of a peer-push-debit transaction.
  */
@@ -214,7 +347,10 @@ async function processPeerPushDebitReady(
     pursePub,
   });
   runLongpollAsync(ws, retryTag, async (ct) => {
-    const mergeUrl = new URL(`purses/${pursePub}/merge`);
+    const mergeUrl = new URL(
+      `purses/${pursePub}/merge`,
+      peerPushInitiation.exchangeBaseUrl,
+    );
     mergeUrl.searchParams.set("timeout_ms", "30000");
     const resp = await ws.http.fetch(mergeUrl.href, {
       // timeout: getReserveRequestTimeout(withdrawalGroup),
@@ -226,16 +362,30 @@ async function processPeerPushDebitReady(
         codecForExchangePurseStatus(),
       );
       if (purseStatus.deposit_timestamp) {
-        await transitionPeerPushDebitFromReadyToDone(
+        await transitionPeerPushDebitTransaction(
           ws,
           peerPushInitiation.pursePub,
+          {
+            stFrom: PeerPushPaymentInitiationStatus.PendingReady,
+            stTo: PeerPushPaymentInitiationStatus.Done,
+          },
         );
         return {
           ready: true,
         };
       }
     } else if (resp.status === HttpStatusCode.Gone) {
-      // FIXME: transition the reserve into the expired state
+      await transitionPeerPushDebitTransaction(
+        ws,
+        peerPushInitiation.pursePub,
+        {
+          stFrom: PeerPushPaymentInitiationStatus.PendingReady,
+          stTo: PeerPushPaymentInitiationStatus.Expired,
+        },
+      );
+      return {
+        ready: true,
+      };
     }
     return {
       ready: false,
@@ -280,6 +430,10 @@ export async function processPeerPushDebit(
       return processPeerPushDebitCreateReserve(ws, peerPushInitiation);
     case PeerPushPaymentInitiationStatus.PendingReady:
       return processPeerPushDebitReady(ws, peerPushInitiation);
+    case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+      return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation);
+    case PeerPushPaymentInitiationStatus.AbortingRefresh:
+      return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation);
   }
 
   return {
@@ -396,7 +550,8 @@ export async function initiatePeerPushDebit(
     mergePriv: mergePair.priv,
     pursePub: pursePair.pub,
     exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
-    talerUri: constructPayPushUri({
+    talerUri: stringifyTalerUri({
+      type: TalerUriAction.PayPush,
       exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
       contractPriv: contractKeyPair.priv,
     }),
@@ -431,6 +586,8 @@ export function computePeerPushDebitTransactionActions(
       return [TransactionAction.Suspend, TransactionAction.Abort];
     case PeerPushPaymentInitiationStatus.Done:
       return [TransactionAction.Delete];
+    case PeerPushPaymentInitiationStatus.Expired:
+      return [TransactionAction.Delete];
     case PeerPushPaymentInitiationStatus.Failed:
       return [TransactionAction.Delete];
   }
@@ -474,9 +631,9 @@ export async function abortPeerPushDebitTransaction(
         case PeerPushPaymentInitiationStatus.Done:
         case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
         case PeerPushPaymentInitiationStatus.Aborted:
-          // Do nothing
-          break;
+        case PeerPushPaymentInitiationStatus.Expired:
         case PeerPushPaymentInitiationStatus.Failed:
+          // Do nothing
           break;
         default:
           assertUnreachable(pushDebitRec.status);
@@ -535,6 +692,7 @@ export async function failPeerPushDebitTransaction(
         case PeerPushPaymentInitiationStatus.Done:
         case PeerPushPaymentInitiationStatus.Aborted:
         case PeerPushPaymentInitiationStatus.Failed:
+        case PeerPushPaymentInitiationStatus.Expired:
           // Do nothing
           break;
         default:
@@ -598,6 +756,7 @@ export async function suspendPeerPushDebitTransaction(
         case PeerPushPaymentInitiationStatus.Done:
         case PeerPushPaymentInitiationStatus.Aborted:
         case PeerPushPaymentInitiationStatus.Failed:
+        case PeerPushPaymentInitiationStatus.Expired:
           // Do nothing
           break;
         default:
@@ -660,6 +819,7 @@ export async function resumePeerPushDebitTransaction(
         case PeerPushPaymentInitiationStatus.Done:
         case PeerPushPaymentInitiationStatus.Aborted:
         case PeerPushPaymentInitiationStatus.Failed:
+        case PeerPushPaymentInitiationStatus.Expired:
           // Do nothing
           break;
         default:
@@ -681,7 +841,6 @@ export async function resumePeerPushDebitTransaction(
   notifyTransition(ws, transactionId, transitionInfo);
 }
 
-
 export function computePeerPushDebitTransactionState(
   ppiRecord: PeerPushPaymentInitiationRecord,
 ): TransactionState {
@@ -738,5 +897,10 @@ export function computePeerPushDebitTransactionState(
       return {
         major: TransactionMajorState.Failed,
       };
+    case PeerPushPaymentInitiationStatus.Expired:
+      return {
+        major: TransactionMajorState.Expired,
+      };
   }
-}
\ No newline at end of file
+}
+

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