gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: DD37 wip


From: gnunet
Subject: [taler-wallet-core] branch master updated: DD37 wip
Date: Fri, 05 May 2023 13:22:25 +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 60805f3ff DD37 wip
60805f3ff is described below

commit 60805f3ff88d7ecd149a88ec7cea846384155c5b
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri May 5 10:56:42 2023 +0200

    DD37 wip
---
 packages/taler-util/src/transactions-types.ts      |  10 +-
 packages/taler-wallet-cli/src/index.ts             |   2 +-
 packages/taler-wallet-core/src/db.ts               |  75 ++-
 .../taler-wallet-core/src/internal-wallet-state.ts |   2 +-
 .../src/operations/backup/import.ts                |   2 +-
 .../taler-wallet-core/src/operations/common.ts     |   2 +-
 .../taler-wallet-core/src/operations/deposits.ts   |   4 +-
 .../src/operations/pay-merchant.ts                 |   3 +-
 .../taler-wallet-core/src/operations/pay-peer.ts   | 672 ++++++++++++++++++---
 .../taler-wallet-core/src/operations/pending.ts    |  10 +-
 .../taler-wallet-core/src/operations/recoup.ts     |   2 +-
 .../taler-wallet-core/src/operations/refresh.ts    |  12 +-
 .../src/operations/transactions.ts                 |  32 +-
 .../taler-wallet-core/src/operations/withdraw.ts   | 134 ++--
 packages/taler-wallet-core/src/pending-types.ts    |   9 +-
 packages/taler-wallet-core/src/util/retries.ts     |  12 +-
 packages/taler-wallet-core/src/wallet.ts           |  12 +-
 17 files changed, 761 insertions(+), 234 deletions(-)

diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index 998500f12..8c5b59f5e 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -89,7 +89,6 @@ export enum TransactionMajorState {
   Aborting = "aborting",
   Aborted = "aborted",
   Suspended = "suspended",
-  SuspendedDeletable = "suspended-deletable",
   Dialog = "dialog",
   SuspendedAborting = "suspended-aborting",
   Failed = "failed",
@@ -105,6 +104,7 @@ export enum TransactionMinorState {
   Deposit = "deposit",
   KycRequired = "kyc",
   AmlRequired = "aml",
+  MergeKycRequired = "merge-kyc",
   Track = "track",
   Pay = "pay",
   RebindSession = "rebind-session",
@@ -113,8 +113,13 @@ export enum TransactionMinorState {
   AutoRefund = "auto-refund",
   User = "user",
   Bank = "bank",
+  Exchange = "exchange",
   ClaimProposal = "claim-proposal",
   CheckRefunds = "check-refunds",
+  CreatePurse = "create-purse",
+  DeletePurse = "delete-purse",
+  Ready = "ready",
+  Merge = "merge",
   Repurchase = "repurchase",
   BankRegisterReserve = "bank-register-reserve",
   BankConfirmTransfer = "bank-confirm-transfer",
@@ -122,6 +127,9 @@ export enum TransactionMinorState {
   ExchangeWaitReserve = "exchange-wait-reserve",
   AbortingBank = "aborting-bank",
   Refused = "refused",
+  Withdraw = "withdraw",
+  MerchantOrderProposed = "merchant-order-proposed",
+  Proposed = "proposed",
 }
 
 export interface TransactionsResponse {
diff --git a/packages/taler-wallet-cli/src/index.ts 
b/packages/taler-wallet-cli/src/index.ts
index e58ea4c8d..996cfc861 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -444,7 +444,7 @@ transactionsCli
   });
 
 transactionsCli
-  .subcommand("cancelAbortingTransaction", "suspend", {
+  .subcommand("cancelAbortingTransaction", "cancel-aborting", {
     help: "Cancel the attempt of properly aborting a transaction.",
   })
   .requiredArgument("transactionId", clk.STRING, {
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index febc5f8fa..a95db9ca3 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -130,10 +130,10 @@ export enum OperationStatusRange {
   // Operations that need to be actively processed.
   ACTIVE_START = 10,
   ACTIVE_END = 29,
-  // Operations that need user input, but nothing can be done
-  // automatically.
-  USER_ATTENTION_START = 30,
-  USER_ATTENTION_END = 49,
+  // Operations that are suspended and might
+  // expire, but nothing else can be done.
+  SUSPENDED_START = 30,
+  SUSPENDED_END = 49,
   // Operations that don't need any attention or processing.
   DORMANT_START = 50,
   DORMANT_END = 69,
@@ -146,24 +146,24 @@ export enum WithdrawalGroupStatus {
   /**
    * Reserve must be registered with the bank.
    */
-  RegisteringBank = 10,
+  PendingRegisteringBank = 10,
 
   /**
    * We've registered reserve's information with the bank
    * and are now waiting for the user to confirm the withdraw
    * with the bank (typically 2nd factor auth).
    */
-  WaitConfirmBank = 11,
+  PendingWaitConfirmBank = 11,
 
   /**
    * Querying reserve status with the exchange.
    */
-  QueryingStatus = 12,
+  PendingQueryingStatus = 12,
 
   /**
    * Ready for withdrawal.
    */
-  Ready = 13,
+  PendingReady = 13,
 
   /**
    * We are telling the bank that we don't want to complete
@@ -174,12 +174,20 @@ export enum WithdrawalGroupStatus {
   /**
    * Exchange wants KYC info from the user.
    */
-  Kyc = 16,
+  PendingKyc = 16,
 
   /**
    * Exchange is doing AML checks.
    */
-  Aml = 17,
+  PendingAml = 17,
+
+  SuspendedRegisteringBank = 30,
+  SuspendedWaitConfirmBank = 31,
+  SuspendedQueryingStatus = 32,
+  SuspendedReady = 33,
+  SuspendedAbortingBank = 34,
+  SuspendedKyc = 35,
+  SuspendedAml = 36,
 
   /**
    * The corresponding withdraw record has been created.
@@ -191,16 +199,16 @@ export enum WithdrawalGroupStatus {
   /**
    * The bank aborted the withdrawal.
    */
-  BankAborted = 51,
+  FailedBankAborted = 51,
 
-  SuspendedRegisteringBank = 52,
-  SuspendedWaitConfirmBank = 53,
-  SuspendedQueryingStatus = 54,
-  SuspendedReady = 55,
-  SuspendedAbortingBank = 56,
-  SuspendedKyc = 57,
-  SuspendedAml = 58,
   FailedAbortingBank = 59,
+
+  /**
+   * Aborted in a state where we were supposed to
+   * talk to the exchange.  Money might have been
+   * wired or not.
+   */
+  AbortedExchange = 60
 }
 
 /**
@@ -1790,8 +1798,18 @@ export enum PeerPushPaymentInitiationStatus {
   /**
    * Initiated, but no purse created yet.
    */
-  Initiated = 10 /* ACTIVE_START */,
-  PurseCreated = 50 /* DORMANT_START */,
+  PendingCreatePurse = 10 /* ACTIVE_START */,
+  PendingReady = 11,
+  AbortingDeletePurse = 12,
+  AbortingRefresh = 13,
+
+  SuspendedCreatePurse = 30,
+  SuspendedReady = 31,
+  SuspendedAbortingDeletePurse = 32,
+  SuspendedAbortingRefresh = 33,
+
+  Done = 50 /* DORMANT_START */,
+  Aborted = 51,
 }
 
 export interface PeerPushPaymentCoinSelection {
@@ -1856,14 +1874,18 @@ export interface PeerPushPaymentInitiationRecord {
 }
 
 export enum PeerPullPaymentInitiationStatus {
-  Initial = 10 /* ACTIVE_START */,
+  PendingCreatePurse = 10 /* ACTIVE_START */,
   /**
    * Purse created, waiting for the other party to accept the
    * invoice and deposit money into it.
    */
-  PurseCreated = 11 /* ACTIVE_START + 1 */,
-  KycRequired = 12 /* ACTIVE_START + 2 */,
-  PurseDeposited = 50 /* DORMANT_START */,
+  PendingReady = 11 /* ACTIVE_START + 1 */,
+  PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */,
+  PendingWithdrawing = 13,
+  SuspendedCreatePurse = 30,
+  SuspendedReady = 31,
+  SuspendedWithdrawing = 32,
+  DonePurseDeposited = 50 /* DORMANT_START */,
 }
 
 export interface PeerPullPaymentInitiationRecord {
@@ -1921,12 +1943,13 @@ export interface PeerPullPaymentInitiationRecord {
 export enum PeerPushPaymentIncomingStatus {
   Proposed = 30 /* USER_ATTENTION_START */,
   Accepted = 10 /* ACTIVE_START */,
-  KycRequired = 11 /* ACTIVE_START + 1 */,
+  MergeKycRequired = 11 /* ACTIVE_START + 1 */,
   /**
    * Merge was successful and withdrawal group has been created, now
    * everything is in the hand of the withdrawal group.
    */
-  WithdrawalCreated = 50 /* DORMANT_START */,
+  Withdrawing = 12,
+  Done = 50 /* DORMANT_START */,
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts 
b/packages/taler-wallet-core/src/internal-wallet-state.ts
index e59781471..760f40d6c 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -177,7 +177,7 @@ export interface InternalWalletState {
    *
    * Used to allow processing of new work faster.
    */
-  latch: AsyncCondition;
+  workAvailable: AsyncCondition;
 
   listeners: NotificationListener[];
 
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 32b90e126..296517162 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -553,7 +553,7 @@ export async function importBackup(
           reservePub,
           status: backupWg.timestamp_finish
             ? WithdrawalGroupStatus.Finished
-            : WithdrawalGroupStatus.QueryingStatus, // FIXME!
+            : WithdrawalGroupStatus.PendingQueryingStatus, // FIXME!
           timestampStart: backupWg.timestamp_created,
           wgInfo,
           restrictAge: backupWg.restrict_age,
diff --git a/packages/taler-wallet-core/src/operations/common.ts 
b/packages/taler-wallet-core/src/operations/common.ts
index 539632b02..55015f2e0 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -492,7 +492,7 @@ export function runLongpollAsync(
     if (!res.ready) {
       await storeOperationPending(ws, retryTag);
     }
-    ws.latch.trigger();
+    ws.workAvailable.trigger();
   };
   asyncFn();
 }
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index e1d699775..b08f03bd1 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -248,7 +248,7 @@ export async function resumeDepositGroup(
       }
       return undefined;
     });
-  ws.latch.trigger();
+  ws.workAvailable.trigger();
   if (res) {
     ws.notify({
       type: NotificationType.TransactionStateTransition,
@@ -301,7 +301,7 @@ export async function abortDepositGroup(
     });
   stopLongpolling(ws, retryTag);
   // Need to process the operation again.
-  ws.latch.trigger();
+  ws.workAvailable.trigger();
   if (res) {
     ws.notify({
       type: NotificationType.TransactionStateTransition,
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts 
b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index d2713dc9d..67343d69a 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -2410,7 +2410,7 @@ export async function processPurchaseQueryRefund(
   return OperationAttemptResult.finishedEmpty();
 }
 
-export async function abortPay(
+export async function abortPayMerchant(
   ws: InternalWalletState,
   proposalId: string,
   cancelImmediately?: boolean,
@@ -2499,6 +2499,7 @@ export function computePayMerchantTransactionState(
     case PurchaseStatus.Proposed:
       return {
         major: TransactionMajorState.Dialog,
+        minor: TransactionMinorState.MerchantOrderProposed,
       };
     case PurchaseStatus.ProposalDownloadFailed:
       return {
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts 
b/packages/taler-wallet-core/src/operations/pay-peer.ts
index 33659afe0..31e395cab 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -75,20 +75,21 @@ import {
   NotificationType,
   HttpStatusCode,
   codecForWalletKycUuid,
-  WalletKycUuid,
+  TransactionState,
+  TransactionMajorState,
+  TransactionMinorState,
 } from "@gnu-taler/taler-util";
 import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
 import {
   DenominationRecord,
-  KycPendingInfo,
-  KycUserType,
-  OperationStatus,
+  PeerPullPaymentIncomingRecord,
   PeerPullPaymentIncomingStatus,
   PeerPullPaymentInitiationRecord,
   PeerPullPaymentInitiationStatus,
   PeerPushPaymentCoinSelection,
   PeerPushPaymentIncomingRecord,
   PeerPushPaymentIncomingStatus,
+  PeerPushPaymentInitiationRecord,
   PeerPushPaymentInitiationStatus,
   ReserveRecord,
   WithdrawalGroupStatus,
@@ -98,7 +99,6 @@ import { TalerError } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import {
   LongpollResult,
-  makeTransactionId,
   resetOperationTimeout,
   runLongpollAsync,
   runOperationWithErrorReporting,
@@ -128,8 +128,10 @@ import {
 import { PendingTaskType } from "../pending-types.js";
 import {
   constructTransactionIdentifier,
+  notifyTransition,
   stopLongpolling,
 } from "./transactions.js";
+import { assertUnreachable } from "../util/assertUnreachable.js";
 
 const logger = new Logger("operations/peer-to-peer.ts");
 
@@ -451,19 +453,11 @@ export async function checkPeerPushDebit(
   };
 }
 
-export async function processPeerPushInitiation(
+async function processPeerPushDebitCreateReserve(
   ws: InternalWalletState,
-  pursePub: string,
+  peerPushInitiation: PeerPushPaymentInitiationRecord,
 ): Promise<OperationAttemptResult> {
-  const peerPushInitiation = await ws.db
-    .mktx((x) => [x.peerPushPaymentInitiations])
-    .runReadOnly(async (tx) => {
-      return tx.peerPushPaymentInitiations.get(pursePub);
-    });
-  if (!peerPushInitiation) {
-    throw Error("peer push payment not found");
-  }
-
+  const pursePub = peerPushInitiation.pursePub;
   const purseExpiration = peerPushInitiation.purseExpiration;
   const hContractTerms = peerPushInitiation.contractTermsHash;
 
@@ -501,22 +495,25 @@ export async function processPeerPushInitiation(
     peerPushInitiation.exchangeBaseUrl,
   );
 
-  const httpResp = await ws.http.postJson(createPurseUrl.href, {
-    amount: peerPushInitiation.amount,
-    merge_pub: peerPushInitiation.mergePub,
-    purse_sig: purseSigResp.sig,
-    h_contract_terms: hContractTerms,
-    purse_expiration: purseExpiration,
-    deposits: depositSigsResp.deposits,
-    min_age: 0,
-    econtract: econtractResp.econtract,
+  const httpResp = await ws.http.fetch(createPurseUrl.href, {
+    method: "POST",
+    body: {
+      amount: peerPushInitiation.amount,
+      merge_pub: peerPushInitiation.mergePub,
+      purse_sig: purseSigResp.sig,
+      h_contract_terms: hContractTerms,
+      purse_expiration: purseExpiration,
+      deposits: depositSigsResp.deposits,
+      min_age: 0,
+      econtract: econtractResp.econtract,
+    },
   });
 
   const resp = await httpResp.json();
 
   logger.info(`resp: ${j2s(resp)}`);
 
-  if (httpResp.status !== 200) {
+  if (httpResp.status !== HttpStatusCode.Ok) {
     throw Error("got error response from exchange");
   }
 
@@ -527,7 +524,7 @@ export async function processPeerPushInitiation(
       if (!ppi) {
         return;
       }
-      ppi.status = PeerPushPaymentInitiationStatus.PurseCreated;
+      ppi.status = PeerPushPaymentInitiationStatus.Done;
       await tx.peerPushPaymentInitiations.put(ppi);
     });
 
@@ -537,6 +534,122 @@ export async function processPeerPushInitiation(
   };
 }
 
+async function transitionPeerPushDebitFromReadyToDone(
+  ws: InternalWalletState,
+  pursePub: string,
+): Promise<void> {
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!ppiRec) {
+        return undefined;
+      }
+      if (ppiRec.status !== PeerPushPaymentInitiationStatus.PendingReady) {
+        return undefined;
+      }
+      const oldTxState = computePeerPushDebitTransactionState(ppiRec);
+      ppiRec.status = PeerPushPaymentInitiationStatus.Done;
+      const newTxState = computePeerPushDebitTransactionState(ppiRec);
+      return {
+        oldTxState,
+        newTxState,
+      };
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+/**
+ * Process the "pending(ready)" state of a peer-push-debit transaction.
+ */
+async function processPeerPushDebitReady(
+  ws: InternalWalletState,
+  peerPushInitiation: PeerPushPaymentInitiationRecord,
+): Promise<OperationAttemptResult> {
+  const pursePub = peerPushInitiation.pursePub;
+  const retryTag = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+  runLongpollAsync(ws, retryTag, async (ct) => {
+    const mergeUrl = new URL(`purses/${pursePub}/merge`);
+    mergeUrl.searchParams.set("timeout_ms", "30000");
+    const resp = await ws.http.fetch(mergeUrl.href, {
+      // timeout: getReserveRequestTimeout(withdrawalGroup),
+      cancellationToken: ct,
+    });
+    if (resp.status === HttpStatusCode.Ok) {
+      const purseStatus = await readSuccessResponseJsonOrThrow(
+        resp,
+        codecForExchangePurseStatus(),
+      );
+      if (purseStatus.deposit_timestamp) {
+        await transitionPeerPushDebitFromReadyToDone(
+          ws,
+          peerPushInitiation.pursePub,
+        );
+        return {
+          ready: true,
+        };
+      }
+    } else if (resp.status === HttpStatusCode.Gone) {
+      // FIXME: transition the reserve into the expired state
+    }
+    return {
+      ready: false,
+    };
+  });
+  logger.trace(
+    "returning early from withdrawal for long-polling in background",
+  );
+  return {
+    type: OperationAttemptResultType.Longpoll,
+  };
+}
+
+export async function processPeerPushDebit(
+  ws: InternalWalletState,
+  pursePub: string,
+): Promise<OperationAttemptResult> {
+  const peerPushInitiation = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadOnly(async (tx) => {
+      return tx.peerPushPaymentInitiations.get(pursePub);
+    });
+  if (!peerPushInitiation) {
+    throw Error("peer push payment not found");
+  }
+
+  const retryTag = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+
+  // We're already running!
+  if (ws.activeLongpoll[retryTag]) {
+    logger.info("peer-push-debit task already in long-polling, returning!");
+    return {
+      type: OperationAttemptResultType.Longpoll,
+    };
+  }
+
+  switch (peerPushInitiation.status) {
+    case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+      return processPeerPushDebitCreateReserve(ws, peerPushInitiation);
+    case PeerPushPaymentInitiationStatus.PendingReady:
+      return processPeerPushDebitReady(ws, peerPushInitiation);
+  }
+
+  return {
+    type: OperationAttemptResultType.Finished,
+    result: undefined,
+  };
+}
+
 /**
  * Initiate sending a peer-to-peer push payment.
  */
@@ -612,7 +725,7 @@ export async function initiatePeerPushPayment(
         pursePriv: pursePair.priv,
         pursePub: pursePair.pub,
         timestampCreated: TalerProtocolTimestamp.now(),
-        status: PeerPushPaymentInitiationStatus.Initiated,
+        status: PeerPushPaymentInitiationStatus.PendingCreatePurse,
         contractTerms: contractTerms,
         coinSel: {
           coinPubs: sel.coins.map((x) => x.coinPub),
@@ -628,12 +741,12 @@ export async function initiatePeerPushPayment(
     });
 
   const taskId = constructTaskIdentifier({
-    tag: PendingTaskType.PeerPushInitiation,
+    tag: PendingTaskType.PeerPushDebit,
     pursePub: pursePair.pub,
   });
 
   await runOperationWithErrorReporting(ws, taskId, async () => {
-    return await processPeerPushInitiation(ws, pursePair.pub);
+    return await processPeerPushDebit(ws, pursePair.pub);
   });
 
   return {
@@ -645,22 +758,24 @@ export async function initiatePeerPushPayment(
       exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
       contractPriv: contractKeyPair.priv,
     }),
-    transactionId: makeTransactionId(
-      TransactionType.PeerPushDebit,
-      pursePair.pub,
-    ),
+    transactionId: constructTransactionIdentifier({
+      tag: TransactionType.PeerPushDebit,
+      pursePub: pursePair.pub,
+    }),
   };
 }
 
 interface ExchangePurseStatus {
   balance: AmountString;
   deposit_timestamp?: TalerProtocolTimestamp;
+  merge_timestamp?: TalerProtocolTimestamp;
 }
 
 export const codecForExchangePurseStatus = (): Codec<ExchangePurseStatus> =>
   buildCodecForObject<ExchangePurseStatus>()
     .property("balance", codecForAmountString())
     .property("deposit_timestamp", codecOptional(codecForTimestamp))
+    .property("merge_timestamp", codecOptional(codecForTimestamp))
     .build("ExchangePurseStatus");
 
 export async function preparePeerPushCredit(
@@ -879,13 +994,13 @@ export async function processPeerPushCredit(
   const amount = Amounts.parseOrThrow(contractTerms.amount);
 
   if (
-    peerInc.status === PeerPushPaymentIncomingStatus.KycRequired &&
+    peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired &&
     peerInc.kycInfo
   ) {
-    const txId = makeTransactionId(
-      TransactionType.PeerPushCredit,
-      peerInc.peerPushPaymentIncomingId,
-    );
+    const txId = constructTransactionIdentifier({
+      tag: TransactionType.PeerPushCredit,
+      peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
+    });
     await checkWithdrawalKycStatus(
       ws,
       peerInc.exchangeBaseUrl,
@@ -951,7 +1066,7 @@ export async function processPeerPushCredit(
           paytoHash: kycPending.h_payto,
           requirementRow: kycPending.requirement_row,
         };
-        peerInc.status = PeerPushPaymentIncomingStatus.KycRequired;
+        peerInc.status = PeerPushPaymentIncomingStatus.MergeKycRequired;
         await tx.peerPushPaymentIncoming.put(peerInc);
       });
     return {
@@ -975,7 +1090,7 @@ export async function processPeerPushCredit(
     },
     forcedWithdrawalGroupId: peerInc.withdrawalGroupId,
     exchangeBaseUrl: peerInc.exchangeBaseUrl,
-    reserveStatus: WithdrawalGroupStatus.QueryingStatus,
+    reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
     reserveKeyPair: {
       priv: mergeReserveInfo.reservePriv,
       pub: mergeReserveInfo.reservePub,
@@ -993,9 +1108,9 @@ export async function processPeerPushCredit(
       }
       if (
         peerInc.status === PeerPushPaymentIncomingStatus.Accepted ||
-        peerInc.status === PeerPushPaymentIncomingStatus.KycRequired
+        peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired
       ) {
-        peerInc.status = PeerPushPaymentIncomingStatus.WithdrawalCreated;
+        peerInc.status = PeerPushPaymentIncomingStatus.Done;
       }
       await tx.peerPushPaymentIncoming.put(peerInc);
     });
@@ -1011,7 +1126,7 @@ export async function confirmPeerPushCredit(
   req: ConfirmPeerPushCreditRequest,
 ): Promise<AcceptPeerPushPaymentResponse> {
   let peerInc: PeerPushPaymentIncomingRecord | undefined;
-  let contractTerms: PeerContractTerms | undefined;
+
   await ws.db
     .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
     .runReadWrite(async (tx) => {
@@ -1021,10 +1136,6 @@ export async function confirmPeerPushCredit(
       if (!peerInc) {
         return;
       }
-      const ctRec = await tx.contractTerms.get(peerInc.contractTermsHash);
-      if (ctRec) {
-        contractTerms = ctRec.contractTermsRaw;
-      }
       if (peerInc.status === PeerPushPaymentIncomingStatus.Proposed) {
         peerInc.status = PeerPushPaymentIncomingStatus.Accepted;
       }
@@ -1037,21 +1148,15 @@ export async function confirmPeerPushCredit(
     );
   }
 
-  checkDbInvariant(!!contractTerms);
-
-  await updateExchangeFromUrl(ws, peerInc.exchangeBaseUrl);
-
-  const retryTag = TaskIdentifiers.forPeerPushCredit(peerInc);
+  ws.workAvailable.trigger();
 
-  await runOperationWithErrorReporting(ws, retryTag, () =>
-    processPeerPushCredit(ws, req.peerPushPaymentIncomingId),
-  );
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushCredit,
+    peerPushPaymentIncomingId: req.peerPushPaymentIncomingId,
+  });
 
   return {
-    transactionId: makeTransactionId(
-      TransactionType.PeerPushCredit,
-      req.peerPushPaymentIncomingId,
-    ),
+    transactionId,
   };
 }
 
@@ -1209,11 +1314,13 @@ export async function confirmPeerPullDebit(
     },
   );
 
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullDebit,
+    peerPullPaymentIncomingId: req.peerPullPaymentIncomingId,
+  });
+
   return {
-    transactionId: makeTransactionId(
-      TransactionType.PeerPullDebit,
-      req.peerPullPaymentIncomingId,
-    ),
+    transactionId,
   };
 }
 
@@ -1395,7 +1502,7 @@ export async function queryPurseForPeerPullCredit(
     },
     forcedWithdrawalGroupId: pullIni.withdrawalGroupId,
     exchangeBaseUrl: pullIni.exchangeBaseUrl,
-    reserveStatus: WithdrawalGroupStatus.QueryingStatus,
+    reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
     reserveKeyPair: {
       priv: reserve.reservePriv,
       pub: reserve.reservePub,
@@ -1410,8 +1517,8 @@ export async function queryPurseForPeerPullCredit(
         logger.warn("peerPullPaymentInitiation not found anymore");
         return;
       }
-      if (finPi.status === PeerPullPaymentInitiationStatus.PurseCreated) {
-        finPi.status = PeerPullPaymentInitiationStatus.PurseDeposited;
+      if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) {
+        finPi.status = PeerPullPaymentInitiationStatus.DonePurseDeposited;
       }
       await tx.peerPullPaymentInitiations.put(finPi);
     });
@@ -1434,7 +1541,7 @@ export async function processPeerPullCredit(
   }
 
   const retryTag = constructTaskIdentifier({
-    tag: PendingTaskType.PeerPullInitiation,
+    tag: PendingTaskType.PeerPullCredit,
     pursePub,
   });
 
@@ -1449,7 +1556,7 @@ export async function processPeerPullCredit(
   logger.trace(`processing ${retryTag}, status=${pullIni.status}`);
 
   switch (pullIni.status) {
-    case PeerPullPaymentInitiationStatus.PurseDeposited: {
+    case PeerPullPaymentInitiationStatus.DonePurseDeposited: {
       // We implement this case so that the "retry" action on a 
peer-pull-credit transaction
       // also retries the withdrawal task.
 
@@ -1475,7 +1582,7 @@ export async function processPeerPullCredit(
         result: undefined,
       };
     }
-    case PeerPullPaymentInitiationStatus.PurseCreated:
+    case PeerPullPaymentInitiationStatus.PendingReady:
       runLongpollAsync(ws, retryTag, async (cancellationToken) =>
         queryPurseForPeerPullCredit(ws, pullIni, cancellationToken),
       );
@@ -1485,23 +1592,23 @@ export async function processPeerPullCredit(
       return {
         type: OperationAttemptResultType.Longpoll,
       };
-    case PeerPullPaymentInitiationStatus.KycRequired: {
+    case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
+      const transactionId = constructTransactionIdentifier({
+        tag: TransactionType.PeerPullCredit,
+        pursePub: pullIni.pursePub,
+      });
       if (pullIni.kycInfo) {
-        const txId = makeTransactionId(
-          TransactionType.PeerPullCredit,
-          pullIni.pursePub,
-        );
         await checkWithdrawalKycStatus(
           ws,
           pullIni.exchangeBaseUrl,
-          txId,
+          transactionId,
           pullIni.kycInfo,
           "individual",
         );
       }
       break;
     }
-    case PeerPullPaymentInitiationStatus.Initial:
+    case PeerPullPaymentInitiationStatus.PendingCreatePurse:
       break;
     default:
       throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`);
@@ -1590,7 +1697,8 @@ export async function processPeerPullCredit(
           paytoHash: kycPending.h_payto,
           requirementRow: kycPending.requirement_row,
         };
-        peerIni.status = PeerPullPaymentInitiationStatus.KycRequired;
+        peerIni.status =
+          PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
         await tx.peerPullPaymentInitiations.put(peerIni);
       });
     return {
@@ -1610,7 +1718,7 @@ export async function processPeerPullCredit(
       if (!pi2) {
         return;
       }
-      pi2.status = PeerPullPaymentInitiationStatus.PurseCreated;
+      pi2.status = PeerPullPaymentInitiationStatus.PendingReady;
       await tx.peerPullPaymentInitiations.put(pi2);
     });
 
@@ -1776,7 +1884,7 @@ export async function initiatePeerPullPayment(
         pursePub: pursePair.pub,
         mergePriv: mergePair.priv,
         mergePub: mergePair.pub,
-        status: PeerPullPaymentInitiationStatus.Initial,
+        status: PeerPullPaymentInitiationStatus.PendingCreatePurse,
         contractTerms: contractTerms,
         mergeTimestamp,
         mergeReserveRowId: mergeReserveRowId,
@@ -1796,7 +1904,7 @@ export async function initiatePeerPullPayment(
   // check this asynchronously from the transaction status?
 
   const taskId = constructTaskIdentifier({
-    tag: PendingTaskType.PeerPullInitiation,
+    tag: PendingTaskType.PeerPullCredit,
     pursePub: pursePair.pub,
   });
 
@@ -1804,14 +1912,408 @@ export async function initiatePeerPullPayment(
     return processPeerPullCredit(ws, pursePair.pub);
   });
 
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPullCredit,
+    pursePub: pursePair.pub,
+  });
+
   return {
     talerUri: constructPayPullUri({
       exchangeBaseUrl: exchangeBaseUrl,
       contractPriv: contractKeyPair.priv,
     }),
-    transactionId: makeTransactionId(
-      TransactionType.PeerPullCredit,
-      pursePair.pub,
-    ),
+    transactionId,
   };
 }
+
+export function computePeerPushDebitTransactionState(
+  ppiRecord: PeerPushPaymentInitiationRecord,
+): TransactionState {
+  switch (ppiRecord.status) {
+    case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.CreatePurse,
+      };
+    case PeerPushPaymentInitiationStatus.PendingReady:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Ready,
+      };
+    case PeerPushPaymentInitiationStatus.Aborted:
+      return {
+        major: TransactionMajorState.Aborted,
+      };
+    case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+      return {
+        major: TransactionMajorState.Aborting,
+        minor: TransactionMinorState.DeletePurse,
+      };
+    case PeerPushPaymentInitiationStatus.AbortingRefresh:
+      return {
+        major: TransactionMajorState.Aborting,
+        minor: TransactionMinorState.Refresh,
+      };
+    case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+      return {
+        major: TransactionMajorState.SuspendedAborting,
+        minor: TransactionMinorState.DeletePurse,
+      };
+    case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+      return {
+        major: TransactionMajorState.SuspendedAborting,
+        minor: TransactionMinorState.Refresh,
+      };
+    case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+      return {
+        major: TransactionMajorState.Suspended,
+        minor: TransactionMinorState.CreatePurse,
+      };
+    case PeerPushPaymentInitiationStatus.SuspendedReady:
+      return {
+        major: TransactionMajorState.Suspended,
+        minor: TransactionMinorState.Ready,
+      };
+    case PeerPushPaymentInitiationStatus.Done:
+      return {
+        major: TransactionMajorState.Done,
+      };
+  }
+}
+
+export async function abortPeerPushDebitTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!pushDebitRec) {
+        logger.warn(`peer push debit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+      switch (pushDebitRec.status) {
+        case PeerPushPaymentInitiationStatus.PendingReady:
+        case PeerPushPaymentInitiationStatus.SuspendedReady:
+          newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+        case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+          // Network request might already be in-flight!
+          newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+        case PeerPushPaymentInitiationStatus.AbortingRefresh:
+        case PeerPushPaymentInitiationStatus.Done:
+        case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+        case PeerPushPaymentInitiationStatus.Aborted:
+          // Do nothing
+          break;
+        default:
+          assertUnreachable(pushDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        pushDebitRec.status = newStatus;
+        const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        await tx.peerPushPaymentInitiations.put(pushDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function cancelAbortingPeerPushDebitTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!pushDebitRec) {
+        logger.warn(`peer push debit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+      switch (pushDebitRec.status) {
+        case PeerPushPaymentInitiationStatus.AbortingRefresh:
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+          // FIXME: We also need to abort the refresh group!
+          newStatus = PeerPushPaymentInitiationStatus.Aborted;
+          break;
+        case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+          newStatus = PeerPushPaymentInitiationStatus.Aborted;
+          break;
+        case PeerPushPaymentInitiationStatus.PendingReady:
+        case PeerPushPaymentInitiationStatus.SuspendedReady:
+        case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+        case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+        case PeerPushPaymentInitiationStatus.Done:
+        case PeerPushPaymentInitiationStatus.Aborted:
+          // Do nothing
+          break;
+        default:
+          assertUnreachable(pushDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        pushDebitRec.status = newStatus;
+        const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        await tx.peerPushPaymentInitiations.put(pushDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function suspendPeerPushDebitTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!pushDebitRec) {
+        logger.warn(`peer push debit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+      switch (pushDebitRec.status) {
+        case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+          newStatus = PeerPushPaymentInitiationStatus.SuspendedCreatePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.AbortingRefresh:
+          newStatus = PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh;
+          break;
+        case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+          newStatus =
+            PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.PendingReady:
+          newStatus = PeerPushPaymentInitiationStatus.SuspendedReady;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+        case PeerPushPaymentInitiationStatus.SuspendedReady:
+        case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+        case PeerPushPaymentInitiationStatus.Done:
+        case PeerPushPaymentInitiationStatus.Aborted:
+          // Do nothing
+          break;
+        default:
+          assertUnreachable(pushDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        pushDebitRec.status = newStatus;
+        const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        await tx.peerPushPaymentInitiations.put(pushDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export async function resumePeerPushDebitTransaction(
+  ws: InternalWalletState,
+  pursePub: string,
+) {
+  const taskId = constructTaskIdentifier({
+    tag: PendingTaskType.PeerPushDebit,
+    pursePub,
+  });
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.PeerPushDebit,
+    pursePub,
+  });
+  stopLongpolling(ws, taskId);
+  const transitionInfo = await ws.db
+    .mktx((x) => [x.peerPushPaymentInitiations])
+    .runReadWrite(async (tx) => {
+      const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+      if (!pushDebitRec) {
+        logger.warn(`peer push debit ${pursePub} not found`);
+        return;
+      }
+      let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+      switch (pushDebitRec.status) {
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+          newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+          newStatus = PeerPushPaymentInitiationStatus.AbortingRefresh;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedReady:
+          newStatus = PeerPushPaymentInitiationStatus.PendingReady;
+          break;
+        case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+          newStatus = PeerPushPaymentInitiationStatus.PendingCreatePurse;
+          break;
+        case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+        case PeerPushPaymentInitiationStatus.AbortingRefresh:
+        case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+        case PeerPushPaymentInitiationStatus.PendingReady:
+        case PeerPushPaymentInitiationStatus.Done:
+        case PeerPushPaymentInitiationStatus.Aborted:
+          // Do nothing
+          break;
+        default:
+          assertUnreachable(pushDebitRec.status);
+      }
+      if (newStatus != null) {
+        const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        pushDebitRec.status = newStatus;
+        const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
+        await tx.peerPushPaymentInitiations.put(pushDebitRec);
+        return {
+          oldTxState,
+          newTxState,
+        };
+      }
+      return undefined;
+    });
+  notifyTransition(ws, transactionId, transitionInfo);
+}
+
+export function computePeerPushCreditTransactionState(
+  pushCreditRecord: PeerPushPaymentIncomingRecord,
+): TransactionState {
+  switch (pushCreditRecord.status) {
+    case PeerPushPaymentIncomingStatus.Proposed:
+      return {
+        major: TransactionMajorState.Dialog,
+        minor: TransactionMinorState.Proposed,
+      };
+    case PeerPushPaymentIncomingStatus.Accepted:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Merge,
+      };
+    case PeerPushPaymentIncomingStatus.Done:
+      return {
+        major: TransactionMajorState.Done,
+      };
+    case PeerPushPaymentIncomingStatus.MergeKycRequired:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.KycRequired,
+      };
+    case PeerPushPaymentIncomingStatus.Withdrawing:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Withdraw,
+      };
+  }
+}
+
+export function computePeerPullCreditTransactionState(
+  pullCreditRecord: PeerPullPaymentInitiationRecord,
+): TransactionState {
+  switch (pullCreditRecord.status) {
+    case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.CreatePurse,
+      };
+    case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.MergeKycRequired,
+      };
+    case PeerPullPaymentInitiationStatus.PendingReady:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Ready,
+      };
+    case PeerPullPaymentInitiationStatus.DonePurseDeposited:
+      return {
+        major: TransactionMajorState.Done,
+      };
+    case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Withdraw,
+      };
+    case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+      return {
+        major: TransactionMajorState.Suspended,
+        minor: TransactionMinorState.CreatePurse,
+      };
+    case PeerPullPaymentInitiationStatus.SuspendedReady:
+      return {
+        major: TransactionMajorState.Suspended,
+        minor: TransactionMinorState.Ready,
+      };
+    case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Withdraw,
+      };
+  }
+}
+
+export function computePeerPullDebitTransactionState(
+  pullDebitRecord: PeerPullPaymentIncomingRecord,
+): TransactionState {
+  switch (pullDebitRecord.status) {
+    case PeerPullPaymentIncomingStatus.Proposed:
+      return {
+        major: TransactionMajorState.Dialog,
+        minor: TransactionMinorState.Proposed,
+      };
+    case PeerPullPaymentIncomingStatus.Accepted:
+      return {
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Deposit,
+      };
+    case PeerPullPaymentIncomingStatus.Paid:
+      return {
+        major: TransactionMajorState.Done,
+      };
+  }
+}
diff --git a/packages/taler-wallet-core/src/operations/pending.ts 
b/packages/taler-wallet-core/src/operations/pending.ts
index 5e14721f8..084f807f2 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -364,14 +364,14 @@ async function gatherPeerPullInitiationPending(
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => {
-    if (pi.status === PeerPullPaymentInitiationStatus.PurseDeposited) {
+    if (pi.status === PeerPullPaymentInitiationStatus.DonePurseDeposited) {
       return;
     }
     const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi);
     const retryRecord = await tx.operationRetries.get(opId);
     const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
     resp.pendingOperations.push({
-      type: PendingTaskType.PeerPullInitiation,
+      type: PendingTaskType.PeerPullCredit,
       ...getPendingCommon(ws, opId, timestampDue),
       givesLifeness: true,
       retryInfo: retryRecord?.retryInfo,
@@ -421,14 +421,14 @@ async function gatherPeerPushInitiationPending(
   resp: PendingOperationsResponse,
 ): Promise<void> {
   await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
-    if (pi.status === PeerPushPaymentInitiationStatus.PurseCreated) {
+    if (pi.status === PeerPushPaymentInitiationStatus.Done) {
       return;
     }
     const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi);
     const retryRecord = await tx.operationRetries.get(opId);
     const timestampDue = retryRecord?.retryInfo.nextRetry ?? 
AbsoluteTime.now();
     resp.pendingOperations.push({
-      type: PendingTaskType.PeerPushInitiation,
+      type: PendingTaskType.PeerPushDebit,
       ...getPendingCommon(ws, opId, timestampDue),
       givesLifeness: true,
       retryInfo: retryRecord?.retryInfo,
@@ -450,7 +450,7 @@ async function gatherPeerPushCreditPending(
     switch (pi.status) {
       case PeerPushPaymentIncomingStatus.Proposed:
         return;
-      case PeerPushPaymentIncomingStatus.WithdrawalCreated:
+      case PeerPushPaymentIncomingStatus.Done:
         return;
     }
     const opId = TaskIdentifiers.forPeerPushCredit(pi);
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts 
b/packages/taler-wallet-core/src/operations/recoup.ts
index 3b423474b..982f3cf8c 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -400,7 +400,7 @@ export async function processRecoupGroupHandler(
     await internalCreateWithdrawalGroup(ws, {
       amount: Amounts.parseOrThrow(result.balance),
       exchangeBaseUrl: recoupGroup.exchangeBaseUrl,
-      reserveStatus: WithdrawalGroupStatus.QueryingStatus,
+      reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
       reserveKeyPair: {
         pub: reservePub,
         priv: reservePrivMap[reservePub],
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 748c929c2..fda9a886a 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -1125,7 +1125,7 @@ export async function autoRefresh(
   return OperationAttemptResult.finishedEmpty();
 }
 
-export function computeRefreshTransactionStatus(
+export function computeRefreshTransactionState(
   rg: RefreshGroupRecord,
 ): TransactionState {
   switch (rg.operationStatus) {
@@ -1170,7 +1170,7 @@ export async function suspendRefreshGroup(
         );
         return undefined;
       }
-      const oldState = computeRefreshTransactionStatus(dg);
+      const oldState = computeRefreshTransactionState(dg);
       switch (dg.operationStatus) {
         case RefreshOperationStatus.Finished:
           return undefined;
@@ -1179,7 +1179,7 @@ export async function suspendRefreshGroup(
           await tx.refreshGroups.put(dg);
           return {
             oldTxState: oldState,
-            newTxState: computeRefreshTransactionStatus(dg),
+            newTxState: computeRefreshTransactionState(dg),
           };
         }
         case RefreshOperationStatus.Suspended:
@@ -1215,7 +1215,7 @@ export async function resumeRefreshGroup(
         );
         return;
       }
-      const oldState = computeRefreshTransactionStatus(dg);
+      const oldState = computeRefreshTransactionState(dg);
       switch (dg.operationStatus) {
         case RefreshOperationStatus.Finished:
           return;
@@ -1227,12 +1227,12 @@ export async function resumeRefreshGroup(
           await tx.refreshGroups.put(dg);
           return {
             oldTxState: oldState,
-            newTxState: computeRefreshTransactionStatus(dg),
+            newTxState: computeRefreshTransactionState(dg),
           };
       }
       return undefined;
     });
-  ws.latch.trigger();
+  ws.workAvailable.trigger();
   if (res) {
     ws.notify({
       type: NotificationType.TransactionStateTransition,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index c122bb651..02f11d82d 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -87,14 +87,14 @@ import {
 } from "./deposits.js";
 import { getExchangeDetails } from "./exchanges.js";
 import {
-  abortPay,
+  abortPayMerchant,
   computePayMerchantTransactionState,
   expectProposalDownload,
   extractContractData,
   processPurchasePay,
 } from "./pay-merchant.js";
-import { processPeerPullCredit } from "./pay-peer.js";
-import { processRefreshGroup } from "./refresh.js";
+import { computePeerPullCreditTransactionState, 
computePeerPullDebitTransactionState, computePeerPushCreditTransactionState, 
computePeerPushDebitTransactionState, processPeerPullCredit } from 
"./pay-peer.js";
+import { computeRefreshTransactionState, processRefreshGroup } from 
"./refresh.js";
 import { computeTipTransactionStatus, processTip } from "./tip.js";
 import {
   abortWithdrawalTransaction,
@@ -445,7 +445,7 @@ function buildTransactionForPushPaymentDebit(
 ): Transaction {
   return {
     type: TransactionType.PeerPushDebit,
-    txState: mkTxStateUnknown(),
+    txState: computePeerPushDebitTransactionState(pi),
     amountEffective: pi.totalCost,
     amountRaw: pi.amount,
     exchangeBaseUrl: pi.exchangeBaseUrl,
@@ -455,10 +455,10 @@ function buildTransactionForPushPaymentDebit(
     },
     frozen: false,
     extendedStatus:
-      pi.status != PeerPushPaymentInitiationStatus.PurseCreated
+      pi.status != PeerPushPaymentInitiationStatus.Done
         ? ExtendedStatus.Pending
         : ExtendedStatus.Done,
-    pending: pi.status != PeerPushPaymentInitiationStatus.PurseCreated,
+    pending: pi.status != PeerPushPaymentInitiationStatus.Done,
     timestamp: pi.timestampCreated,
     talerUri: constructPayPushUri({
       exchangeBaseUrl: pi.exchangeBaseUrl,
@@ -478,7 +478,7 @@ function buildTransactionForPullPaymentDebit(
 ): Transaction {
   return {
     type: TransactionType.PeerPullDebit,
-    txState: mkTxStateUnknown(),
+    txState: computePeerPullDebitTransactionState(pi),
     amountEffective: pi.coinSel?.totalCost
       ? pi.coinSel?.totalCost
       : Amounts.stringify(pi.contractTerms.amount),
@@ -528,7 +528,7 @@ function buildTransactionForPeerPullCredit(
       });
     return {
       type: TransactionType.PeerPullCredit,
-      txState: mkTxStateUnknown(),
+      txState: computePeerPullCreditTransactionState(pullCredit),
       amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
       amountRaw: Amounts.stringify(wsr.instructedAmount),
       exchangeBaseUrl: wsr.exchangeBaseUrl,
@@ -563,7 +563,7 @@ function buildTransactionForPeerPullCredit(
 
   return {
     type: TransactionType.PeerPullCredit,
-    txState: mkTxStateUnknown(),
+    txState: computePeerPullCreditTransactionState(pullCredit),
     amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
     amountRaw: Amounts.stringify(peerContractTerms.amount),
     exchangeBaseUrl: pullCredit.exchangeBaseUrl,
@@ -602,7 +602,7 @@ function buildTransactionForPeerPushCredit(
 
     return {
       type: TransactionType.PeerPushCredit,
-      txState: mkTxStateUnknown(),
+      txState: computePeerPushCreditTransactionState(pushInc),
       amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
       amountRaw: Amounts.stringify(wsr.instructedAmount),
       exchangeBaseUrl: wsr.exchangeBaseUrl,
@@ -626,7 +626,7 @@ function buildTransactionForPeerPushCredit(
 
   return {
     type: TransactionType.PeerPushCredit,
-    txState: mkTxStateUnknown(),
+    txState: computePeerPushCreditTransactionState(pushInc),
     // FIXME: This is wrong, needs to consider fees!
     amountEffective: Amounts.stringify(peerContractTerms.amount),
     amountRaw: Amounts.stringify(peerContractTerms.amount),
@@ -666,7 +666,7 @@ function buildTransactionForBankIntegratedWithdraw(
       bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl,
       reserveIsReady:
         wgRecord.status === WithdrawalGroupStatus.Finished ||
-        wgRecord.status === WithdrawalGroupStatus.Ready,
+        wgRecord.status === WithdrawalGroupStatus.PendingReady,
     },
     exchangeBaseUrl: wgRecord.exchangeBaseUrl,
     extendedStatus: wgRecord.timestampFinish
@@ -713,7 +713,7 @@ function buildTransactionForManualWithdraw(
       exchangePaytoUris,
       reserveIsReady:
         withdrawalGroup.status === WithdrawalGroupStatus.Finished ||
-        withdrawalGroup.status === WithdrawalGroupStatus.Ready,
+        withdrawalGroup.status === WithdrawalGroupStatus.PendingReady,
     },
     exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl,
     extendedStatus: withdrawalGroup.timestampFinish
@@ -753,7 +753,7 @@ function buildTransactionForRefresh(
   ).amount;
   return {
     type: TransactionType.Refresh,
-    txState: mkTxStateUnknown(),
+    txState: computeRefreshTransactionState(refreshGroupRecord),
     refreshReason: refreshGroupRecord.reason,
     amountEffective: Amounts.stringify(
       Amounts.zeroOfCurrency(refreshGroupRecord.currency),
@@ -1538,7 +1538,7 @@ export async function retryTransaction(
   switch (parsedTx.tag) {
     case TransactionType.PeerPullCredit: {
       const taskId = constructTaskIdentifier({
-        tag: PendingTaskType.PeerPullInitiation,
+        tag: PendingTaskType.PeerPullCredit,
         pursePub: parsedTx.pursePub,
       });
       await resetOperationTimeout(ws, taskId);
@@ -1866,7 +1866,7 @@ export async function abortTransaction(
 
   switch (txId.tag) {
     case TransactionType.Payment: {
-      await abortPay(ws, txId.proposalId);
+      await abortPayMerchant(ws, txId.proposalId);
       break;
     }
     case TransactionType.Withdrawal: {
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index d1816de03..d0c4d453f 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -135,6 +135,7 @@ import {
   notifyTransition,
   stopLongpolling,
 } from "./transactions.js";
+import { assertUnreachable } from "../util/assertUnreachable.js";
 
 /**
  * Logger for this file.
@@ -160,25 +161,25 @@ export async function suspendWithdrawalTransaction(
       }
       let newStatus: WithdrawalGroupStatus | undefined = undefined;
       switch (wg.status) {
-        case WithdrawalGroupStatus.Ready:
+        case WithdrawalGroupStatus.PendingReady:
           newStatus = WithdrawalGroupStatus.SuspendedReady;
           break;
         case WithdrawalGroupStatus.AbortingBank:
           newStatus = WithdrawalGroupStatus.SuspendedAbortingBank;
           break;
-        case WithdrawalGroupStatus.WaitConfirmBank:
+        case WithdrawalGroupStatus.PendingWaitConfirmBank:
           newStatus = WithdrawalGroupStatus.SuspendedWaitConfirmBank;
           break;
-        case WithdrawalGroupStatus.RegisteringBank:
+        case WithdrawalGroupStatus.PendingRegisteringBank:
           newStatus = WithdrawalGroupStatus.SuspendedRegisteringBank;
           break;
-        case WithdrawalGroupStatus.QueryingStatus:
-          newStatus = WithdrawalGroupStatus.QueryingStatus;
+        case WithdrawalGroupStatus.PendingQueryingStatus:
+          newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
           break;
-        case WithdrawalGroupStatus.Kyc:
+        case WithdrawalGroupStatus.PendingKyc:
           newStatus = WithdrawalGroupStatus.SuspendedKyc;
           break;
-        case WithdrawalGroupStatus.Aml:
+        case WithdrawalGroupStatus.PendingAml:
           newStatus = WithdrawalGroupStatus.SuspendedAml;
           break;
         default:
@@ -221,25 +222,25 @@ export async function resumeWithdrawalTransaction(
       let newStatus: WithdrawalGroupStatus | undefined = undefined;
       switch (wg.status) {
         case WithdrawalGroupStatus.SuspendedReady:
-          newStatus = WithdrawalGroupStatus.Ready;
+          newStatus = WithdrawalGroupStatus.PendingReady;
           break;
         case WithdrawalGroupStatus.SuspendedAbortingBank:
           newStatus = WithdrawalGroupStatus.AbortingBank;
           break;
         case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
-          newStatus = WithdrawalGroupStatus.WaitConfirmBank;
+          newStatus = WithdrawalGroupStatus.PendingWaitConfirmBank;
           break;
         case WithdrawalGroupStatus.SuspendedQueryingStatus:
-          newStatus = WithdrawalGroupStatus.QueryingStatus;
+          newStatus = WithdrawalGroupStatus.PendingQueryingStatus;
           break;
         case WithdrawalGroupStatus.SuspendedRegisteringBank:
-          newStatus = WithdrawalGroupStatus.RegisteringBank;
+          newStatus = WithdrawalGroupStatus.PendingRegisteringBank;
           break;
         case WithdrawalGroupStatus.SuspendedAml:
-          newStatus = WithdrawalGroupStatus.Aml;
+          newStatus = WithdrawalGroupStatus.PendingAml;
           break;
         case WithdrawalGroupStatus.SuspendedKyc:
-          newStatus = WithdrawalGroupStatus.Kyc;
+          newStatus = WithdrawalGroupStatus.PendingKyc;
           break;
         default:
           logger.warn(
@@ -289,21 +290,21 @@ export async function abortWithdrawalTransaction(
       }
       let newStatus: WithdrawalGroupStatus | undefined = undefined;
       switch (wg.status) {
-        case WithdrawalGroupStatus.WaitConfirmBank:
-        case WithdrawalGroupStatus.RegisteringBank:
+        case WithdrawalGroupStatus.PendingWaitConfirmBank:
+        case WithdrawalGroupStatus.PendingRegisteringBank:
         case WithdrawalGroupStatus.AbortingBank:
           newStatus = WithdrawalGroupStatus.AbortingBank;
           break;
-        case WithdrawalGroupStatus.Aml:
+        case WithdrawalGroupStatus.PendingAml:
           newStatus = WithdrawalGroupStatus.SuspendedAml;
           break;
-        case WithdrawalGroupStatus.Kyc:
+        case WithdrawalGroupStatus.PendingKyc:
           newStatus = WithdrawalGroupStatus.SuspendedKyc;
           break;
-        case WithdrawalGroupStatus.QueryingStatus:
+        case WithdrawalGroupStatus.PendingQueryingStatus:
           newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
           break;
-        case WithdrawalGroupStatus.Ready:
+        case WithdrawalGroupStatus.PendingReady:
           newStatus = WithdrawalGroupStatus.SuspendedReady;
           break;
         case WithdrawalGroupStatus.SuspendedAbortingBank:
@@ -316,9 +317,13 @@ export async function abortWithdrawalTransaction(
         case WithdrawalGroupStatus.SuspendedRegisteringBank:
         case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
         case WithdrawalGroupStatus.Finished:
-        case WithdrawalGroupStatus.BankAborted:
+        case WithdrawalGroupStatus.FailedBankAborted:
+        case WithdrawalGroupStatus.AbortedExchange:
+        case WithdrawalGroupStatus.FailedAbortingBank:
           // Not allowed
           break;
+        default:
+          assertUnreachable(wg.status);
       }
       if (newStatus != null) {
         const oldTxState = computeWithdrawalTransactionStatus(wg);
@@ -385,7 +390,7 @@ export function computeWithdrawalTransactionStatus(
   wgRecord: WithdrawalGroupRecord,
 ): TransactionState {
   switch (wgRecord.status) {
-    case WithdrawalGroupStatus.BankAborted:
+    case WithdrawalGroupStatus.FailedBankAborted:
       return {
         major: TransactionMajorState.Aborted,
       };
@@ -393,22 +398,22 @@ export function computeWithdrawalTransactionStatus(
       return {
         major: TransactionMajorState.Done,
       };
-    case WithdrawalGroupStatus.RegisteringBank:
+    case WithdrawalGroupStatus.PendingRegisteringBank:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.BankRegisterReserve,
       };
-    case WithdrawalGroupStatus.Ready:
+    case WithdrawalGroupStatus.PendingReady:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.WithdrawCoins,
       };
-    case WithdrawalGroupStatus.QueryingStatus:
+    case WithdrawalGroupStatus.PendingQueryingStatus:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.ExchangeWaitReserve,
       };
-    case WithdrawalGroupStatus.WaitConfirmBank:
+    case WithdrawalGroupStatus.PendingWaitConfirmBank:
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.BankConfirmTransfer,
@@ -444,13 +449,13 @@ export function computeWithdrawalTransactionStatus(
         minor: TransactionMinorState.WithdrawCoins,
       };
     }
-    case WithdrawalGroupStatus.Aml: {
+    case WithdrawalGroupStatus.PendingAml: {
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.AmlRequired,
       };
     }
-    case WithdrawalGroupStatus.Kyc: {
+    case WithdrawalGroupStatus.PendingKyc: {
       return {
         major: TransactionMajorState.Pending,
         minor: TransactionMinorState.KycRequired,
@@ -473,6 +478,11 @@ export function computeWithdrawalTransactionStatus(
         major: TransactionMajorState.Failed,
         minor: TransactionMinorState.AbortingBank,
       };
+    case WithdrawalGroupStatus.AbortedExchange:
+      return {
+        major: TransactionMajorState.Aborted,
+        minor: TransactionMinorState.Exchange,
+      }
   }
 }
 
@@ -1122,7 +1132,7 @@ async function queryReserve(
     withdrawalGroupId,
   });
   checkDbInvariant(!!withdrawalGroup);
-  if (withdrawalGroup.status !== WithdrawalGroupStatus.QueryingStatus) {
+  if (withdrawalGroup.status !== WithdrawalGroupStatus.PendingQueryingStatus) {
     return { ready: true };
   }
   const reservePub = withdrawalGroup.reservePub;
@@ -1135,7 +1145,7 @@ async function queryReserve(
 
   logger.info(`querying reserve status via ${reserveUrl.href}`);
 
-  const resp = await ws.http.get(reserveUrl.href, {
+  const resp = await ws.http.fetch(reserveUrl.href, {
     timeout: getReserveRequestTimeout(withdrawalGroup),
     cancellationToken,
   });
@@ -1177,7 +1187,7 @@ async function queryReserve(
         return undefined;
       }
       const txStateOld = computeWithdrawalTransactionStatus(wg);
-      wg.status = WithdrawalGroupStatus.Ready;
+      wg.status = WithdrawalGroupStatus.PendingReady;
       const txStateNew = computeWithdrawalTransactionStatus(wg);
       wg.reserveBalanceAmount = Amounts.stringify(result.response.balance);
       await tx.withdrawalGroups.put(wg);
@@ -1250,12 +1260,12 @@ export async function processWithdrawalGroup(
   }
 
   switch (withdrawalGroup.status) {
-    case WithdrawalGroupStatus.RegisteringBank:
+    case WithdrawalGroupStatus.PendingRegisteringBank:
       await processReserveBankStatus(ws, withdrawalGroupId);
       return await processWithdrawalGroup(ws, withdrawalGroupId, {
         forceNow: true,
       });
-    case WithdrawalGroupStatus.QueryingStatus: {
+    case WithdrawalGroupStatus.PendingQueryingStatus: {
       runLongpollAsync(ws, retryTag, (ct) => {
         return queryReserve(ws, withdrawalGroupId, ct);
       });
@@ -1266,7 +1276,7 @@ export async function processWithdrawalGroup(
         type: OperationAttemptResultType.Longpoll,
       };
     }
-    case WithdrawalGroupStatus.WaitConfirmBank: {
+    case WithdrawalGroupStatus.PendingWaitConfirmBank: {
       const res = await processReserveBankStatus(ws, withdrawalGroupId);
       switch (res.status) {
         case BankStatusResultCode.Aborted:
@@ -1284,7 +1294,7 @@ export async function processWithdrawalGroup(
       }
       break;
     }
-    case WithdrawalGroupStatus.BankAborted: {
+    case WithdrawalGroupStatus.FailedBankAborted: {
       // FIXME
       return {
         type: OperationAttemptResultType.Pending,
@@ -1294,7 +1304,7 @@ export async function processWithdrawalGroup(
     case WithdrawalGroupStatus.Finished:
       // We can try to withdraw, nothing needs to be done with the reserve.
       break;
-    case WithdrawalGroupStatus.Ready:
+    case WithdrawalGroupStatus.PendingReady:
       // Continue with the actual withdrawal!
       break;
     default:
@@ -1847,8 +1857,8 @@ async function registerReserveWithBank(
     withdrawalGroupId,
   });
   switch (withdrawalGroup?.status) {
-    case WithdrawalGroupStatus.WaitConfirmBank:
-    case WithdrawalGroupStatus.RegisteringBank:
+    case WithdrawalGroupStatus.PendingWaitConfirmBank:
+    case WithdrawalGroupStatus.PendingRegisteringBank:
       break;
     default:
       return;
@@ -1885,8 +1895,8 @@ async function registerReserveWithBank(
         return undefined;
       }
       switch (r.status) {
-        case WithdrawalGroupStatus.RegisteringBank:
-        case WithdrawalGroupStatus.WaitConfirmBank:
+        case WithdrawalGroupStatus.PendingRegisteringBank:
+        case WithdrawalGroupStatus.PendingWaitConfirmBank:
           break;
         default:
           return;
@@ -1898,7 +1908,7 @@ async function registerReserveWithBank(
         AbsoluteTime.now(),
       );
       const oldTxState = computeWithdrawalTransactionStatus(r);
-      r.status = WithdrawalGroupStatus.WaitConfirmBank;
+      r.status = WithdrawalGroupStatus.PendingWaitConfirmBank;
       const newTxState = computeWithdrawalTransactionStatus(r);
       await tx.withdrawalGroups.put(r);
       return {
@@ -1928,8 +1938,8 @@ async function processReserveBankStatus(
     withdrawalGroupId,
   });
   switch (withdrawalGroup?.status) {
-    case WithdrawalGroupStatus.WaitConfirmBank:
-    case WithdrawalGroupStatus.RegisteringBank:
+    case WithdrawalGroupStatus.PendingWaitConfirmBank:
+    case WithdrawalGroupStatus.PendingRegisteringBank:
       break;
     default:
       return {
@@ -1969,8 +1979,8 @@ async function processReserveBankStatus(
           return;
         }
         switch (r.status) {
-          case WithdrawalGroupStatus.RegisteringBank:
-          case WithdrawalGroupStatus.WaitConfirmBank:
+          case WithdrawalGroupStatus.PendingRegisteringBank:
+          case WithdrawalGroupStatus.PendingWaitConfirmBank:
             break;
           default:
             return;
@@ -1981,7 +1991,7 @@ async function processReserveBankStatus(
         const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
         const oldTxState = computeWithdrawalTransactionStatus(r);
         r.wgInfo.bankInfo.timestampBankConfirmed = now;
-        r.status = WithdrawalGroupStatus.BankAborted;
+        r.status = WithdrawalGroupStatus.FailedBankAborted;
         const newTxState = computeWithdrawalTransactionStatus(r);
         await tx.withdrawalGroups.put(r);
         return {
@@ -2002,7 +2012,7 @@ async function processReserveBankStatus(
   }
 
   // FIXME: Why do we do this?!
-  if (withdrawalGroup.status === WithdrawalGroupStatus.RegisteringBank) {
+  if (withdrawalGroup.status === WithdrawalGroupStatus.PendingRegisteringBank) 
{
     await registerReserveWithBank(ws, withdrawalGroupId);
     return await processReserveBankStatus(ws, withdrawalGroupId);
   }
@@ -2016,8 +2026,8 @@ async function processReserveBankStatus(
       }
       // Re-check reserve status within transaction
       switch (r.status) {
-        case WithdrawalGroupStatus.RegisteringBank:
-        case WithdrawalGroupStatus.WaitConfirmBank:
+        case WithdrawalGroupStatus.PendingRegisteringBank:
+        case WithdrawalGroupStatus.PendingWaitConfirmBank:
           break;
         default:
           return undefined;
@@ -2030,7 +2040,7 @@ async function processReserveBankStatus(
         logger.info("withdrawal: transfer confirmed by bank.");
         const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
         r.wgInfo.bankInfo.timestampBankConfirmed = now;
-        r.status = WithdrawalGroupStatus.QueryingStatus;
+        r.status = WithdrawalGroupStatus.PendingQueryingStatus;
         // FIXME: Notification is deprecated with DD37.
         ws.notify({
           type: NotificationType.WithdrawalGroupBankConfirmed,
@@ -2276,7 +2286,7 @@ export async function acceptWithdrawalFromUri(
     },
     restrictAge: req.restrictAge,
     forcedDenomSel: req.forcedDenomSel,
-    reserveStatus: WithdrawalGroupStatus.RegisteringBank,
+    reserveStatus: WithdrawalGroupStatus.PendingRegisteringBank,
   });
 
   const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
@@ -2291,19 +2301,14 @@ export async function acceptWithdrawalFromUri(
   const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
     withdrawalGroupId,
   });
-  if (processedWithdrawalGroup?.status === WithdrawalGroupStatus.BankAborted) {
+  if (processedWithdrawalGroup?.status === 
WithdrawalGroupStatus.FailedBankAborted) {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
       {},
     );
   }
 
-  // Start withdrawal in the background
-  processWithdrawalGroup(ws, withdrawalGroupId, {
-    forceNow: true,
-  }).catch((err) => {
-    logger.error("Processing withdrawal (after creation) failed:", err);
-  });
+  ws.workAvailable.trigger();
 
   return {
     reservePub: withdrawalGroup.reservePub,
@@ -2337,7 +2342,7 @@ export async function createManualWithdrawal(
     exchangeBaseUrl: req.exchangeBaseUrl,
     forcedDenomSel: req.forcedDenomSel,
     restrictAge: req.restrictAge,
-    reserveStatus: WithdrawalGroupStatus.QueryingStatus,
+    reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
   });
 
   const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
@@ -2357,18 +2362,7 @@ export async function createManualWithdrawal(
       return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
     });
 
-  // Start withdrawal in the background (do not await!)
-  // FIXME: We could also interrupt the task look if it is waiting and
-  // rely on retry handling to re-process the withdrawal group.
-  runOperationWithErrorReporting(
-    ws,
-    TaskIdentifiers.forWithdrawal(withdrawalGroup),
-    async () => {
-      return await processWithdrawalGroup(ws, withdrawalGroupId, {
-        forceNow: true,
-      });
-    },
-  );
+  ws.workAvailable.trigger();
 
   return {
     reservePub: withdrawalGroup.reservePub,
diff --git a/packages/taler-wallet-core/src/pending-types.ts 
b/packages/taler-wallet-core/src/pending-types.ts
index 0e83ef38c..23f9ae21f 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -37,9 +37,8 @@ export enum PendingTaskType {
   Withdraw = "withdraw",
   Deposit = "deposit",
   Backup = "backup",
-  // FIXME: Rename to peer-push-debit and peer-pull-debit
-  PeerPushInitiation = "peer-push-initiation",
-  PeerPullInitiation = "peer-pull-initiation",
+  PeerPushDebit = "peer-push-debit",
+  PeerPullCredit = "peer-pull-credit",
   PeerPushCredit = "peer-push-credit",
   PeerPullDebit = "peer-pull-debit",
 }
@@ -83,7 +82,7 @@ export interface PendingExchangeUpdateTask {
  * The wallet wants to send a peer push payment.
  */
 export interface PendingPeerPushInitiationTask {
-  type: PendingTaskType.PeerPushInitiation;
+  type: PendingTaskType.PeerPushDebit;
   pursePub: string;
 }
 
@@ -91,7 +90,7 @@ export interface PendingPeerPushInitiationTask {
  * The wallet wants to send a peer pull payment.
  */
 export interface PendingPeerPullInitiationTask {
-  type: PendingTaskType.PeerPullInitiation;
+  type: PendingTaskType.PeerPullCredit;
   pursePub: string;
 }
 
diff --git a/packages/taler-wallet-core/src/util/retries.ts 
b/packages/taler-wallet-core/src/util/retries.ts
index a021087be..12e1df7e9 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -197,9 +197,9 @@ export type ParsedTaskIdentifier =
   | { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string }
   | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string }
   | { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string }
-  | { tag: PendingTaskType.PeerPullInitiation; pursePub: string }
+  | { tag: PendingTaskType.PeerPullCredit; pursePub: string }
   | { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string }
-  | { tag: PendingTaskType.PeerPushInitiation; pursePub: string }
+  | { tag: PendingTaskType.PeerPushDebit; pursePub: string }
   | { tag: PendingTaskType.Purchase; proposalId: string }
   | { tag: PendingTaskType.Recoup; recoupGroupId: string }
   | { tag: PendingTaskType.TipPickup; walletTipId: string }
@@ -223,9 +223,9 @@ export function constructTaskIdentifier(p: 
ParsedTaskIdentifier): string {
       return `${p.tag}:${p.peerPullPaymentIncomingId}`;
     case PendingTaskType.PeerPushCredit:
       return `${p.tag}:${p.peerPushPaymentIncomingId}`;
-    case PendingTaskType.PeerPullInitiation:
+    case PendingTaskType.PeerPullCredit:
       return `${p.tag}:${p.pursePub}`;
-    case PendingTaskType.PeerPushInitiation:
+    case PendingTaskType.PeerPushDebit:
       return `${p.tag}:${p.pursePub}`;
     case PendingTaskType.Purchase:
       return `${p.tag}:${p.proposalId}`;
@@ -276,12 +276,12 @@ export namespace TaskIdentifiers {
   export function forPeerPushPaymentInitiation(
     ppi: PeerPushPaymentInitiationRecord,
   ): string {
-    return `${PendingTaskType.PeerPushInitiation}:${ppi.pursePub}`;
+    return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}`;
   }
   export function forPeerPullPaymentInitiation(
     ppi: PeerPullPaymentInitiationRecord,
   ): string {
-    return `${PendingTaskType.PeerPullInitiation}:${ppi.pursePub}`;
+    return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}`;
   }
   export function forPeerPullPaymentDebit(
     ppi: PeerPullPaymentIncomingRecord,
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 733c239f9..ed174e33b 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -208,7 +208,7 @@ import {
   processPeerPullCredit,
   processPeerPullDebit,
   processPeerPushCredit,
-  processPeerPushInitiation,
+  processPeerPushDebit,
 } from "./operations/pay-peer.js";
 import { getPendingOperations } from "./operations/pending.js";
 import {
@@ -314,9 +314,9 @@ async function callOperationHandler(
     }
     case PendingTaskType.Backup:
       return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
-    case PendingTaskType.PeerPushInitiation:
-      return await processPeerPushInitiation(ws, pending.pursePub);
-    case PendingTaskType.PeerPullInitiation:
+    case PendingTaskType.PeerPushDebit:
+      return await processPeerPushDebit(ws, pending.pursePub);
+    case PendingTaskType.PeerPullCredit:
       return await processPeerPullCredit(ws, pending.pursePub);
     case PendingTaskType.PeerPullDebit:
       return await processPeerPullDebit(ws, pending.peerPullPaymentIncomingId);
@@ -439,7 +439,7 @@ async function runTaskLoop(
       });
       // Wait until either the timeout, or we are notified (via the latch)
       // that more work might be available.
-      await Promise.race([timeout, ws.latch.wait()]);
+      await Promise.race([timeout, ws.workAvailable.wait()]);
     } else {
       logger.trace(
         `running ${pending.pendingOperations.length} pending operations`,
@@ -1659,7 +1659,7 @@ class InternalWalletStateImpl implements 
InternalWalletState {
   merchantInfoCache: Record<string, MerchantInfo> = {};
 
   readonly timerGroup: TimerGroup;
-  latch = new AsyncCondition();
+  workAvailable = new AsyncCondition();
   stopped = false;
 
   listeners: NotificationListener[] = [];

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