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: towards DD37 for


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: towards DD37 for deposits
Date: Sat, 22 Apr 2023 14:17:54 +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 15feebecf wallet-core: towards DD37 for deposits
15feebecf is described below

commit 15feebecfeeda4758a96d1da99a98d9494c4bd2b
Author: Florian Dold <florian@dold.me>
AuthorDate: Sat Apr 22 14:17:49 2023 +0200

    wallet-core: towards DD37 for deposits
---
 packages/taler-harness/src/harness/harness.ts      |  48 ++++++++-
 packages/taler-harness/src/index.ts                |  70 ++++++++++++-
 .../src/integrationtests/test-deposit.ts           |  22 +++-
 .../src/integrationtests/test-wallet-dbless.ts     |  27 +++--
 packages/taler-util/src/notifications.ts           |   4 +-
 packages/taler-util/src/transaction-test-data.ts   |  13 +--
 packages/taler-util/src/transactions-types.ts      |  29 +++---
 packages/taler-wallet-core/src/db.ts               |  10 +-
 packages/taler-wallet-core/src/dbless.ts           |  24 +++--
 .../taler-wallet-core/src/operations/deposits.ts   | 114 +++++++++++++++------
 .../src/operations/transactions.ts                 |  56 +++++-----
 packages/taler-wallet-core/src/util/retries.ts     |   6 ++
 12 files changed, 313 insertions(+), 110 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 0a898414d..8b74c9e9f 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -970,6 +970,19 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
   }
 
+  async runAggregatorOnceWithTimetravel(opts: {
+    timetravelMicroseconds: number;
+  }) {
+    let timetravelArgArr = [];
+    timetravelArgArr.push(`--timetravel=${opts.timetravelMicroseconds}`);
+    await runCommand(
+      this.globalState,
+      `exchange-${this.name}-aggregator-once`,
+      "taler-exchange-aggregator",
+      [...timetravelArgArr, "-c", this.configFilename, "-t"],
+    );
+  }
+
   async runAggregatorOnce() {
     try {
       await runCommand(
@@ -1147,6 +1160,9 @@ export class ExchangeService implements 
ExchangeServiceInterface {
   exchangeHttpProc: ProcessWrapper | undefined;
   exchangeWirewatchProc: ProcessWrapper | undefined;
 
+  exchangeTransferProc: ProcessWrapper | undefined;
+  exchangeAggregatorProc: ProcessWrapper | undefined;
+
   helperCryptoRsaProc: ProcessWrapper | undefined;
   helperCryptoEddsaProc: ProcessWrapper | undefined;
   helperCryptoCsProc: ProcessWrapper | undefined;
@@ -1200,6 +1216,18 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       await wirewatch.wait();
       this.exchangeWirewatchProc = undefined;
     }
+    const aggregatorProc = this.exchangeAggregatorProc;
+    if (aggregatorProc) {
+      aggregatorProc.proc.kill("SIGTERM");
+      await aggregatorProc.wait();
+      this.exchangeAggregatorProc = undefined;
+    }
+    const transferProc = this.exchangeTransferProc;
+    if (transferProc) {
+      transferProc.proc.kill("SIGTERM");
+      await transferProc.wait();
+      this.exchangeTransferProc = undefined;
+    }
     const httpd = this.exchangeHttpProc;
     if (httpd) {
       httpd.proc.kill("SIGTERM");
@@ -1369,6 +1397,22 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
   }
 
+  private internalCreateAggregatorProc() {
+    this.exchangeAggregatorProc = this.globalState.spawnService(
+      "taler-exchange-aggregator",
+      ["-c", this.configFilename, ...this.timetravelArgArr],
+      `exchange-aggregator-${this.name}`,
+    );
+  }
+
+  private internalCreateTransferProc() {
+    this.exchangeTransferProc = this.globalState.spawnService(
+      "taler-exchange-transfer",
+      ["-c", this.configFilename, ...this.timetravelArgArr],
+      `exchange-transfer-${this.name}`,
+    );
+  }
+
   async start(): Promise<void> {
     if (this.isRunning()) {
       throw Error("exchange is already running");
@@ -1398,6 +1442,8 @@ export class ExchangeService implements 
ExchangeServiceInterface {
     );
 
     this.internalCreateWirewatchProc();
+    this.internalCreateTransferProc();
+    this.internalCreateAggregatorProc();
 
     this.exchangeHttpProc = this.globalState.spawnService(
       "taler-exchange-httpd",
@@ -2062,7 +2108,7 @@ export class WalletService {
       [
         "--wallet-db",
         dbPath,
-        "-LDEBUG", // FIXME: Make this configurable?
+        "-LTRACE", // FIXME: Make this configurable?
         "--no-throttle", // FIXME: Optionally do throttling for some tests?
         "advanced",
         "serve",
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index 59fa80411..287e1f5be 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -47,7 +47,14 @@ import { lintExchangeDeployment } from "./lint.js";
 import { runEnvFull } from "./env-full.js";
 import { clk } from "@gnu-taler/taler-util/clk";
 import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
-import { BankAccessApiClient } from "@gnu-taler/taler-wallet-core";
+import {
+  BankAccessApiClient,
+  checkReserve,
+  CryptoDispatcher,
+  downloadExchangeInfo,
+  SynchronousCryptoWorkerFactoryPlain,
+  topupReserveWithDemobank,
+} from "@gnu-taler/taler-wallet-core";
 
 const logger = new Logger("taler-harness:index.ts");
 
@@ -162,7 +169,6 @@ advancedCli
     await runTestWithState(testState, runEnv1, "env1", true);
   });
 
-
 const sandcastleCli = testingCli.subcommand("sandcastleArgs", "sandcastle", {
   help: "Subcommands for handling GNU Taler sandcastle deployments.",
 });
@@ -260,6 +266,66 @@ deploymentCli
     // FIXME: Now delete reserves that are not filled yet
   });
 
+deploymentCli
+  .subcommand("testTalerdotnetDemo", "test-demo-talerdotnet")
+  .action(async (args) => {
+    const http = createPlatformHttpLib();
+    const cryptiDisp = new CryptoDispatcher(
+      new SynchronousCryptoWorkerFactoryPlain(),
+    );
+    const cryptoApi = cryptiDisp.cryptoApi;
+    const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
+    const exchangeBaseUrl = "https://exchange.demo.taler.net/";;
+    const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
+    await topupReserveWithDemobank({
+      amount: "KUDOS:10",
+      bankAccessApiBaseUrl:
+        "https://bank.demo.taler.net/demobanks/default/access-api/";,
+      bankBaseUrl: "",
+      exchangeInfo,
+      http,
+      reservePub: reserveKeyPair.pub,
+    });
+    let reserveUrl = new URL(`reserves/${reserveKeyPair.pub}`, 
exchangeBaseUrl);
+    reserveUrl.searchParams.set("timeout_ms", "30000");
+    console.log("requesting", reserveUrl.href);
+    const longpollReq = http.fetch(reserveUrl.href, {
+      method: "GET",
+    });
+    const reserveStatusResp = await longpollReq;
+    console.log("reserve status", reserveStatusResp.status);
+  });
+
+deploymentCli
+  .subcommand("testLocalhostDemo", "test-demo-localhost")
+  .action(async (args) => {
+    // Run checks against the "env-full" demo deployment on localhost
+    const http = createPlatformHttpLib();
+    const cryptiDisp = new CryptoDispatcher(
+      new SynchronousCryptoWorkerFactoryPlain(),
+    );
+    const cryptoApi = cryptiDisp.cryptoApi;
+    const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
+    const exchangeBaseUrl = "http://localhost:8081/";;
+    const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
+    await topupReserveWithDemobank({
+      amount: "TESTKUDOS:10",
+      bankAccessApiBaseUrl: "http://localhost:8082/taler-bank-access/";,
+      bankBaseUrl: "",
+      exchangeInfo,
+      http,
+      reservePub: reserveKeyPair.pub,
+    });
+    let reserveUrl = new URL(`reserves/${reserveKeyPair.pub}`, 
exchangeBaseUrl);
+    reserveUrl.searchParams.set("timeout_ms", "30000");
+    console.log("requesting", reserveUrl.href);
+    const longpollReq = http.fetch(reserveUrl.href, {
+      method: "GET",
+    });
+    const reserveStatusResp = await longpollReq;
+    console.log("reserve status", reserveStatusResp.status);
+  });
+
 deploymentCli
   .subcommand("tipStatus", "tip-status")
   .requiredOption("merchantBaseUrl", ["--merchant-url"], clk.STRING)
diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts 
b/packages/taler-harness/src/integrationtests/test-deposit.ts
index 6aa086107..8ea3fc12e 100644
--- a/packages/taler-harness/src/integrationtests/test-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-deposit.ts
@@ -17,7 +17,11 @@
 /**
  * Imports.
  */
-import { NotificationType, TransactionState } from "@gnu-taler/taler-util";
+import {
+  NotificationType,
+  TransactionMajorState,
+  TransactionMinorState,
+} from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, getPayto } from "../harness/harness.js";
 import {
@@ -52,11 +56,19 @@ export async function runDepositTest(t: GlobalTestState) {
 
   const depositTxId = dgIdResp.transactionId;
 
+  const depositTrack = walletClient.waitForNotificationCond(
+    (n) =>
+      n.type == NotificationType.TransactionStateTransition &&
+      n.transactionId == depositTxId &&
+      n.newTxState.major == TransactionMajorState.Pending &&
+      n.newTxState.minor == TransactionMinorState.Track,
+  );
+
   const depositDone = walletClient.waitForNotificationCond(
     (n) =>
       n.type == NotificationType.TransactionStateTransition &&
       n.transactionId == depositTxId &&
-      n.newTxState == TransactionState.Done,
+      n.newTxState.major == TransactionMajorState.Done,
   );
 
   const depositGroupResult = await walletClient.client.call(
@@ -70,6 +82,12 @@ export async function runDepositTest(t: GlobalTestState) {
 
   t.assertDeepEqual(depositGroupResult.transactionId, depositTxId);
 
+  await depositTrack;
+
+  await exchange.runAggregatorOnceWithTimetravel({
+    timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3,
+  });
+
   await depositDone;
 
   const transactions = await walletClient.client.call(
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts 
b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
index a1de7617b..35c60a89d 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-dbless.ts
@@ -59,16 +59,29 @@ export async function runWalletDblessTest(t: 
GlobalTestState) {
 
     const reserveKeyPair = await cryptoApi.createEddsaKeypair({});
 
-    await topupReserveWithDemobank(
+    let reserveUrl = new URL(
+      `reserves/${reserveKeyPair.pub}`,
+      exchange.baseUrl,
+    );
+    reserveUrl.searchParams.set("timeout_ms", "30000");
+    const longpollReq = http.fetch(reserveUrl.href, {
+      method: "GET",
+    });
+
+    await topupReserveWithDemobank({
+      amount: "TESTKUDOS:10",
       http,
-      reserveKeyPair.pub,
-      bank.baseUrl,
-      bank.bankAccessApiBaseUrl,
+      reservePub: reserveKeyPair.pub,
+      bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl,
+      bankBaseUrl: bank.baseUrl,
       exchangeInfo,
-      "TESTKUDOS:10",
-    );
+    });
+
+    console.log("waiting for longpoll request");
+    const resp = await longpollReq;
+    console.log(`got response, status ${resp.status}`);
 
-    await exchange.runWirewatchOnce();
+    console.log(exchangeInfo);
 
     await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub);
 
diff --git a/packages/taler-util/src/notifications.ts 
b/packages/taler-util/src/notifications.ts
index ff1017cd1..f0683b31b 100644
--- a/packages/taler-util/src/notifications.ts
+++ b/packages/taler-util/src/notifications.ts
@@ -22,7 +22,7 @@
 /**
  * Imports.
  */
-import { TransactionState, TransactionSubstate } from 
"./transactions-types.js";
+import { TransactionState } from "./transactions-types.js";
 import { TalerErrorDetail } from "./wallet-types.js";
 
 export enum NotificationType {
@@ -75,9 +75,7 @@ export interface TransactionStateTransitionNotification {
   type: NotificationType.TransactionStateTransition;
   transactionId: string;
   oldTxState: TransactionState;
-  oldTxSubstate: TransactionSubstate;
   newTxState: TransactionState;
-  newTxSubstate: TransactionSubstate;
 }
 
 export interface ProposalAcceptedNotification {
diff --git a/packages/taler-util/src/transaction-test-data.ts 
b/packages/taler-util/src/transaction-test-data.ts
index d0c609147..dc0903f53 100644
--- a/packages/taler-util/src/transaction-test-data.ts
+++ b/packages/taler-util/src/transaction-test-data.ts
@@ -16,10 +16,9 @@
 
 import {
   TransactionType,
-  TransactionState,
-  TransactionSubstate,
   PaymentStatus,
   ExtendedStatus,
+  TransactionMajorState,
 } from "./transactions-types.js";
 import { RefreshReason } from "./wallet-types.js";
 
@@ -29,8 +28,9 @@ import { RefreshReason } from "./wallet-types.js";
 export const sampleWalletCoreTransactions = [
   {
     type: TransactionType.Payment,
-    txState: TransactionState.Done,
-    txSubstate: TransactionSubstate.None,
+    txState: {
+      major: TransactionMajorState.Done,
+    },
     amountRaw: "KUDOS:10",
     amountEffective: "KUDOS:10",
     totalRefundRaw: "KUDOS:0",
@@ -75,8 +75,9 @@ export const sampleWalletCoreTransactions = [
   },
   {
     type: TransactionType.Refresh,
-    txState: TransactionState.Pending,
-    txSubstate: TransactionSubstate.None,
+    txState: {
+      major: TransactionMajorState.Pending,
+    },
     refreshReason: RefreshReason.PayMerchant,
     amountEffective: "KUDOS:0",
     amountRaw: "KUDOS:0",
diff --git a/packages/taler-util/src/transactions-types.ts 
b/packages/taler-util/src/transactions-types.ts
index 474372c4f..29ddb22a3 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -59,11 +59,6 @@ export enum ExtendedStatus {
   KycRequired = "kyc-required",
 }
 
-export interface TransactionStateInfo {
-  txState: TransactionState;
-  txSubstate: TransactionSubstate;
-}
-
 export interface TransactionsRequest {
   /**
    * return only transactions in the given currency
@@ -81,7 +76,12 @@ export interface TransactionsRequest {
   includeRefreshes?: boolean;
 }
 
-export enum TransactionState {
+export interface TransactionState {
+  major: TransactionMajorState;
+  minor?: TransactionMinorState;
+}
+
+export enum TransactionMajorState {
   // No state, only used when reporting transitions into the initial state
   None = "none",
   Pending = "pending",
@@ -96,15 +96,13 @@ export enum TransactionState {
   Unknown = "unknown",
 }
 
-export enum TransactionSubstate {
+export enum TransactionMinorState {
   // Placeholder until D37 is fully implemented
   Unknown = "unknown",
-  // No substate
-  None = "none",
-  DepositPendingInitial = "initial",
-  DepositKycRequired = "kyc-required",
-  DepositPendingTrack = "track",
-  DepositAbortingRefresh = "refresh",
+  Deposit = "deposit",
+  KycRequired = "kyc-required",
+  Track = "track",
+  Refresh = "refresh",
 }
 
 export interface TransactionsResponse {
@@ -126,10 +124,11 @@ export interface TransactionCommon {
   // main timestamp of the transaction
   timestamp: TalerProtocolTimestamp;
 
+  /**
+   * Transaction state, as per DD37.
+   */
   txState: TransactionState;
 
-  txSubstate: TransactionSubstate;
-
   /**
    * @deprecated in favor of statusMajor and statusMinor
    */
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index f5342b4cd..0bfe11aaa 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -865,8 +865,10 @@ export enum DepositGroupOperationStatus {
   AbortingWithRefresh = 11 /* ACTIVE_START + 1 */,
 }
 
-// FIXME: Improve name! This enum is very specific to deposits.
-export enum TransactionStatus {
+/**
+ * Status of a single element of a deposit group.
+ */
+export enum DepositElementStatus {
   Unknown = 10,
   Accepted = 20,
   KycRequired = 30,
@@ -1686,7 +1688,7 @@ export interface DepositGroupRecord {
 
   operationStatus: OperationStatus;
 
-  transactionPerCoin: TransactionStatus[];
+  transactionPerCoin: DepositElementStatus[];
 
   trackingState?: {
     [signature: string]: {
@@ -2605,7 +2607,7 @@ export const walletDbFixups: FixupDescription[] = [
           return;
         }
         dg.transactionPerCoin = dg.depositedPerCoin.map(
-          (c) => TransactionStatus.Unknown,
+          (c) => DepositElementStatus.Unknown,
         );
         await tx.depositGroups.put(dg);
       });
diff --git a/packages/taler-wallet-core/src/dbless.ts 
b/packages/taler-wallet-core/src/dbless.ts
index 30c4247a8..3fb56924d 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -109,14 +109,26 @@ export async function checkReserve(
   }
 }
 
+export interface TopupReserveWithDemobankArgs {
+  http: HttpRequestLibrary;
+  reservePub: string;
+  bankBaseUrl: string;
+  bankAccessApiBaseUrl: string;
+  exchangeInfo: ExchangeInfo;
+  amount: AmountString;
+}
+
 export async function topupReserveWithDemobank(
-  http: HttpRequestLibrary,
-  reservePub: string,
-  bankBaseUrl: string,
-  bankAccessApiBaseUrl: string,
-  exchangeInfo: ExchangeInfo,
-  amount: AmountString,
+  args: TopupReserveWithDemobankArgs,
 ) {
+  const {
+    bankBaseUrl,
+    http,
+    bankAccessApiBaseUrl,
+    amount,
+    exchangeInfo,
+    reservePub,
+  } = args;
   const bankHandle: BankServiceHandle = {
     baseUrl: bankBaseUrl,
     bankAccessApiBaseUrl: bankAccessApiBaseUrl,
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index f5ea41e01..6e56b0897 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -40,6 +40,7 @@ import {
   j2s,
   Logger,
   MerchantContractTerms,
+  NotificationType,
   parsePaytoUri,
   PayCoinSelection,
   PrepareDepositRequest,
@@ -49,9 +50,9 @@ import {
   TalerErrorCode,
   TalerProtocolTimestamp,
   TrackTransaction,
+  TransactionMajorState,
+  TransactionMinorState,
   TransactionState,
-  TransactionStateInfo,
-  TransactionSubstate,
   TransactionType,
   URL,
   WireFee,
@@ -60,13 +61,16 @@ import {
   DenominationRecord,
   DepositGroupRecord,
   OperationStatus,
-  TransactionStatus,
+  DepositElementStatus,
 } from "../db.js";
 import { TalerError } from "@gnu-taler/taler-util";
 import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { OperationAttemptResult } from "../util/retries.js";
+import {
+  OperationAttemptResult,
+  OperationAttemptResultType,
+} from "../util/retries.js";
 import { spendCoins } from "./common.js";
 import { getExchangeDetails } from "./exchanges.js";
 import {
@@ -89,15 +93,13 @@ const logger = new Logger("deposits.ts");
  * Get the (DD37-style) transaction status based on the
  * database record of a deposit group.
  */
-export async function computeDepositTransactionStatus(
-  ws: InternalWalletState,
+export function computeDepositTransactionStatus(
   dg: DepositGroupRecord,
-): Promise<TransactionStateInfo> {
+): TransactionState {
   switch (dg.operationStatus) {
     case OperationStatus.Finished: {
       return {
-        txState: TransactionState.Done,
-        txSubstate: TransactionSubstate.None,
+        major: TransactionMajorState.Done,
       };
     }
     case OperationStatus.Pending: {
@@ -110,10 +112,10 @@ export async function computeDepositTransactionStatus(
           numDeposited++;
         }
         switch (dg.transactionPerCoin[i]) {
-          case TransactionStatus.KycRequired:
+          case DepositElementStatus.KycRequired:
             numKycRequired++;
             break;
-          case TransactionStatus.Wired:
+          case DepositElementStatus.Wired:
             numWired++;
             break;
         }
@@ -121,21 +123,21 @@ export async function computeDepositTransactionStatus(
 
       if (numKycRequired > 0) {
         return {
-          txState: TransactionState.Pending,
-          txSubstate: TransactionSubstate.DepositKycRequired,
+          major: TransactionMajorState.Pending,
+          minor: TransactionMinorState.KycRequired,
         };
       }
 
       if (numDeposited == numTotal) {
         return {
-          txState: TransactionState.Pending,
-          txSubstate: TransactionSubstate.DepositPendingTrack,
+          major: TransactionMajorState.Pending,
+          minor: TransactionMinorState.Track,
         };
       }
 
       return {
-        txState: TransactionState.Pending,
-        txSubstate: TransactionSubstate.DepositPendingInitial,
+        major: TransactionMajorState.Pending,
+        minor: TransactionMinorState.Deposit,
       };
     }
     default:
@@ -221,6 +223,13 @@ export async function processDepositGroup(
     return OperationAttemptResult.finishedEmpty();
   }
 
+  const transactionId = constructTransactionIdentifier({
+    tag: TransactionType.Deposit,
+    depositGroupId,
+  });
+
+  const txStateOld = computeDepositTransactionStatus(depositGroup);
+
   const contractData = extractContractData(
     depositGroup.contractTermsRaw,
     depositGroup.contractTermsHash,
@@ -239,7 +248,7 @@ export async function processDepositGroup(
   for (let i = 0; i < depositPermissions.length; i++) {
     const perm = depositPermissions[i];
 
-    let updatedDeposit: boolean | undefined = undefined;
+    let updatedDeposit: boolean = false;
 
     if (!depositGroup.depositedPerCoin[i]) {
       const requestBody: ExchangeDepositRequest = {
@@ -270,7 +279,7 @@ export async function processDepositGroup(
       updatedDeposit = true;
     }
 
-    let updatedTxStatus: TransactionStatus | undefined = undefined;
+    let updatedTxStatus: DepositElementStatus | undefined = undefined;
     type ValueOf<T> = T[keyof T];
 
     let newWiredTransaction:
@@ -280,12 +289,12 @@ export async function processDepositGroup(
         }
       | undefined;
 
-    if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
-      const track = await trackDepositPermission(ws, depositGroup, perm);
+    if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) {
+      const track = await trackDeposit(ws, depositGroup, perm);
 
       if (track.type === "accepted") {
         if (!track.kyc_ok && track.requirement_row !== undefined) {
-          updatedTxStatus = TransactionStatus.KycRequired;
+          updatedTxStatus = DepositElementStatus.KycRequired;
           const { requirement_row: requirementRow } = track;
           const paytoHash = encodeCrock(
             hashTruncate32(stringToBytes(depositGroup.wire.payto_uri + "\0")),
@@ -297,10 +306,10 @@ export async function processDepositGroup(
             "individual",
           );
         } else {
-          updatedTxStatus = TransactionStatus.Accepted;
+          updatedTxStatus = DepositElementStatus.Accepted;
         }
       } else if (track.type === "wired") {
-        updatedTxStatus = TransactionStatus.Wired;
+        updatedTxStatus = DepositElementStatus.Wired;
 
         const payto = parsePaytoUri(depositGroup.wire.payto_uri);
         if (!payto) {
@@ -327,11 +336,11 @@ export async function processDepositGroup(
           id: track.exchange_sig,
         };
       } else {
-        updatedTxStatus = TransactionStatus.Unknown;
+        updatedTxStatus = DepositElementStatus.Unknown;
       }
     }
 
-    if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
+    if (updatedTxStatus !== undefined || updatedDeposit) {
       await ws.db
         .mktx((x) => [x.depositGroups])
         .runReadWrite(async (tx) => {
@@ -358,18 +367,18 @@ export async function processDepositGroup(
     }
   }
 
-  await ws.db
+  const txStatusNew = await ws.db
     .mktx((x) => [x.depositGroups])
     .runReadWrite(async (tx) => {
       const dg = await tx.depositGroups.get(depositGroupId);
       if (!dg) {
-        return;
+        return undefined;
       }
       let allDepositedAndWired = true;
       for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
         if (
           !depositGroup.depositedPerCoin[i] ||
-          depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired
+          depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired
         ) {
           allDepositedAndWired = false;
           break;
@@ -380,8 +389,36 @@ export async function processDepositGroup(
         dg.operationStatus = OperationStatus.Finished;
         await tx.depositGroups.put(dg);
       }
+      return computeDepositTransactionStatus(dg);
+    });
+
+  if (!txStatusNew) {
+    // Doesn't exist anymore!
+    return OperationAttemptResult.finishedEmpty();
+  }
+
+  // Notify if state transitioned
+  if (
+    txStateOld.major !== txStatusNew.major ||
+    txStateOld.minor !== txStatusNew.minor
+  ) {
+    ws.notify({
+      type: NotificationType.TransactionStateTransition,
+      transactionId,
+      oldTxState: txStateOld,
+      newTxState: txStatusNew,
     });
-  return OperationAttemptResult.finishedEmpty();
+  }
+
+  // FIXME: consider other cases like aborting, suspend, ...
+  if (
+    txStatusNew.major === TransactionMajorState.Pending ||
+    txStatusNew.major === TransactionMajorState.Aborting
+  ) {
+    return OperationAttemptResult.pendingEmpty();
+  } else {
+    return OperationAttemptResult.finishedEmpty();
+  }
 }
 
 async function getExchangeWireFee(
@@ -428,7 +465,7 @@ async function getExchangeWireFee(
   return fee;
 }
 
-async function trackDepositPermission(
+async function trackDeposit(
   ws: InternalWalletState,
   depositGroup: DepositGroupRecord,
   dp: CoinDepositPermission,
@@ -448,6 +485,7 @@ async function trackDepositPermission(
   });
   url.searchParams.set("merchant_sig", sigResp.sig);
   const httpResp = await ws.http.fetch(url.href, { method: "GET" });
+  logger.trace(`deposits response status: ${httpResp.status}`);
   switch (httpResp.status) {
     case HttpStatusCode.Accepted: {
       const accepted = await readSuccessResponseJsonOrThrow(
@@ -710,7 +748,7 @@ export async function createDepositGroup(
     timestampCreated: AbsoluteTime.toTimestamp(now),
     timestampFinished: undefined,
     transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
-      () => TransactionStatus.Unknown,
+      () => DepositElementStatus.Unknown,
     ),
     payCoinSelection: payCoinSel.coinSel,
     payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
@@ -733,7 +771,7 @@ export async function createDepositGroup(
     depositGroupId,
   });
 
-  await ws.db
+  const newTxState = await ws.db
     .mktx((x) => [
       x.depositGroups,
       x.coins,
@@ -752,8 +790,18 @@ export async function createDepositGroup(
         refreshReason: RefreshReason.PayDeposit,
       });
       await tx.depositGroups.put(depositGroup);
+      return computeDepositTransactionStatus(depositGroup);
     });
 
+  ws.notify({
+    type: NotificationType.TransactionStateTransition,
+    transactionId,
+    oldTxState: {
+      major: TransactionMajorState.None,
+    },
+    newTxState,
+  });
+
   return {
     depositGroupId,
     transactionId,
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 6a71b5c1e..884844ba6 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -35,10 +35,9 @@ import {
   Transaction,
   TransactionByIdRequest,
   TransactionIdStr,
+  TransactionMajorState,
   TransactionsRequest,
   TransactionsResponse,
-  TransactionState,
-  TransactionSubstate,
   TransactionType,
   WithdrawalType,
 } from "@gnu-taler/taler-util";
@@ -58,7 +57,7 @@ import {
   WalletContractData,
   PeerPushPaymentInitiationStatus,
   PeerPullPaymentIncomingStatus,
-  TransactionStatus,
+  DepositElementStatus,
   WithdrawalGroupStatus,
   RefreshGroupRecord,
   RefreshOperationStatus,
@@ -79,7 +78,10 @@ import {
   runOperationWithErrorReporting,
   TombstoneTag,
 } from "./common.js";
-import { processDepositGroup } from "./deposits.js";
+import {
+  computeDepositTransactionStatus,
+  processDepositGroup,
+} from "./deposits.js";
 import { getExchangeDetails } from "./exchanges.js";
 import {
   abortPay,
@@ -425,6 +427,11 @@ export async function getTransactionById(
   }
 }
 
+// FIXME: Just a marker helper for unknown states until DD37 is fully 
implemented.
+const mkTxStateUnknown = () => ({
+  major: TransactionMajorState.Unknown,
+});
+
 function buildTransactionForPushPaymentDebit(
   pi: PeerPushPaymentInitiationRecord,
   contractTerms: PeerContractTerms,
@@ -432,8 +439,7 @@ function buildTransactionForPushPaymentDebit(
 ): Transaction {
   return {
     type: TransactionType.PeerPushDebit,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: pi.totalCost,
     amountRaw: pi.amount,
     exchangeBaseUrl: pi.exchangeBaseUrl,
@@ -466,8 +472,7 @@ function buildTransactionForPullPaymentDebit(
 ): Transaction {
   return {
     type: TransactionType.PeerPullDebit,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: pi.coinSel?.totalCost
       ? pi.coinSel?.totalCost
       : Amounts.stringify(pi.contractTerms.amount),
@@ -517,8 +522,7 @@ function buildTransactionForPeerPullCredit(
       });
     return {
       type: TransactionType.PeerPullCredit,
-      txState: TransactionState.Unknown,
-      txSubstate: TransactionSubstate.Unknown,
+      txState: mkTxStateUnknown(),
       amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
       amountRaw: Amounts.stringify(wsr.instructedAmount),
       exchangeBaseUrl: wsr.exchangeBaseUrl,
@@ -553,8 +557,7 @@ function buildTransactionForPeerPullCredit(
 
   return {
     type: TransactionType.PeerPullCredit,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
     amountRaw: Amounts.stringify(peerContractTerms.amount),
     exchangeBaseUrl: pullCredit.exchangeBaseUrl,
@@ -593,8 +596,7 @@ function buildTransactionForPeerPushCredit(
 
     return {
       type: TransactionType.PeerPushCredit,
-      txState: TransactionState.Unknown,
-      txSubstate: TransactionSubstate.Unknown,
+      txState: mkTxStateUnknown(),
       amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
       amountRaw: Amounts.stringify(wsr.instructedAmount),
       exchangeBaseUrl: wsr.exchangeBaseUrl,
@@ -618,8 +620,7 @@ function buildTransactionForPeerPushCredit(
 
   return {
     type: TransactionType.PeerPushCredit,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     // FIXME: This is wrong, needs to consider fees!
     amountEffective: Amounts.stringify(peerContractTerms.amount),
     amountRaw: Amounts.stringify(peerContractTerms.amount),
@@ -649,8 +650,7 @@ function buildTransactionForBankIntegratedWithdraw(
 
   return {
     type: TransactionType.Withdrawal,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
     amountRaw: Amounts.stringify(wsr.instructedAmount),
     withdrawalDetails: {
@@ -696,8 +696,7 @@ function buildTransactionForManualWithdraw(
 
   return {
     type: TransactionType.Withdrawal,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: Amounts.stringify(
       withdrawalGroup.denomsSel.totalCoinValue,
     ),
@@ -748,8 +747,7 @@ function buildTransactionForRefresh(
   ).amount;
   return {
     type: TransactionType.Refresh,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     refreshReason: refreshGroupRecord.reason,
     amountEffective: Amounts.stringify(
       Amounts.zeroOfCurrency(refreshGroupRecord.currency),
@@ -791,8 +789,7 @@ function buildTransactionForDeposit(
 
   return {
     type: TransactionType.Deposit,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: computeDepositTransactionStatus(dg),
     amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
     amountEffective: Amounts.stringify(dg.totalPayCost),
     extendedStatus: dg.timestampFinished
@@ -810,7 +807,7 @@ function buildTransactionForDeposit(
     wireTransferProgress:
       (100 *
         dg.transactionPerCoin.reduce(
-          (prev, cur) => prev + (cur === TransactionStatus.Wired ? 1 : 0),
+          (prev, cur) => prev + (cur === DepositElementStatus.Wired ? 1 : 0),
           0,
         )) /
       dg.transactionPerCoin.length,
@@ -829,8 +826,7 @@ function buildTransactionForTip(
 
   return {
     type: TransactionType.Tip,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
     amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
     extendedStatus: tipRecord.pickedUpTimestamp
@@ -926,8 +922,7 @@ async function buildTransactionForRefund(
 
   return {
     type: TransactionType.Refund,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     info,
     refundedTransactionId: makeTransactionId(
       TransactionType.Payment,
@@ -1030,8 +1025,7 @@ async function buildTransactionForPurchase(
 
   return {
     type: TransactionType.Payment,
-    txState: TransactionState.Unknown,
-    txSubstate: TransactionSubstate.Unknown,
+    txState: mkTxStateUnknown(),
     amountRaw: Amounts.stringify(contractData.amount),
     amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost),
     totalRefundRaw: Amounts.stringify(totalRefund.raw),
diff --git a/packages/taler-wallet-core/src/util/retries.ts 
b/packages/taler-wallet-core/src/util/retries.ts
index 5b6645924..a021087be 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -70,6 +70,12 @@ export namespace OperationAttemptResult {
       result: undefined,
     };
   }
+  export function pendingEmpty(): OperationAttemptResult<unknown, unknown> {
+    return {
+      type: OperationAttemptResultType.Pending,
+      result: undefined,
+    };
+  }
 }
 
 export interface OperationAttemptFinishedResult<T> {

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