gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 03/04: query transaction status for deposit


From: gnunet
Subject: [taler-wallet-core] 03/04: query transaction status for deposit
Date: Sun, 15 Jan 2023 21:50:14 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit fc38d0da958323b994d2e4f8a8f2e9632865557f
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sun Jan 15 17:48:41 2023 -0300

    query transaction status for deposit
---
 packages/taler-wallet-core/src/db.ts               |  23 +++
 .../taler-wallet-core/src/operations/deposits.ts   | 200 ++++++++++++++-------
 .../src/operations/transactions.ts                 |   8 +
 3 files changed, 163 insertions(+), 68 deletions(-)

diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index adf704bc4..e6131334c 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -850,6 +850,13 @@ export enum RefreshOperationStatus {
   FinishedWithError = 51 /* DORMANT_START + 1 */,
 }
 
+export enum TransactionStatus {
+  Unknown = 10,
+  Accepted = 20,
+  KycRequired = 30,
+  Wired = 40,
+}
+
 /**
  * Additional information about the reason of a refresh.
  */
@@ -1652,6 +1659,8 @@ export interface DepositGroupRecord {
   timestampFinished: TalerProtocolTimestamp | undefined;
 
   operationStatus: OperationStatus;
+
+  transactionPerCoin: TransactionStatus[];
 }
 
 /**
@@ -2416,6 +2425,20 @@ export const walletDbFixups: FixupDescription[] = [
       });
     },
   },
+  {
+    name: "DepositGroupRecord_transactionPerCoin",
+    async fn(tx): Promise<void> {
+      await tx.depositGroups.iter().forEachAsync(async (dg) => {
+        if (dg.transactionPerCoin) {
+          return;
+        }
+        dg.transactionPerCoin = dg.depositedPerCoin.map(
+          (c) => TransactionStatus.Unknown,
+        );
+        await tx.depositGroups.put(dg);
+      });
+    },
+  },
 ];
 
 const logger = new Logger("db.ts");
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 649621948..b529e5ead 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -24,7 +24,9 @@ import {
   CancellationToken,
   canonicalJson,
   codecForDepositSuccess,
-  MerchantContractTerms,
+  codecForTackTransactionAccepted,
+  codecForTackTransactionWired,
+  CoinDepositPermission,
   CreateDepositGroupRequest,
   CreateDepositGroupResponse,
   DepositGroupFees,
@@ -34,23 +36,27 @@ import {
   GetFeeForDepositRequest,
   getRandomBytes,
   hashWire,
+  HttpStatusCode,
   Logger,
+  MerchantContractTerms,
   parsePaytoUri,
   PayCoinSelection,
   PrepareDepositRequest,
   PrepareDepositResponse,
   RefreshReason,
+  TalerErrorCode,
   TalerProtocolTimestamp,
   TrackDepositGroupRequest,
   TrackDepositGroupResponse,
+  TrackTransaction,
   TransactionType,
   URL,
-  TalerErrorCode,
 } from "@gnu-taler/taler-util";
 import {
   DenominationRecord,
   DepositGroupRecord,
   OperationStatus,
+  TransactionStatus,
 } from "../db.js";
 import { TalerError } from "../errors.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
@@ -111,43 +117,60 @@ export async function processDepositGroup(
   );
 
   for (let i = 0; i < depositPermissions.length; i++) {
-    if (depositGroup.depositedPerCoin[i]) {
-      continue;
-    }
     const perm = depositPermissions[i];
-    const requestBody: ExchangeDepositRequest = {
-      contribution: Amounts.stringify(perm.contribution),
-      merchant_payto_uri: depositGroup.wire.payto_uri,
-      wire_salt: depositGroup.wire.salt,
-      h_contract_terms: depositGroup.contractTermsHash,
-      ub_sig: perm.ub_sig,
-      timestamp: depositGroup.contractTermsRaw.timestamp,
-      wire_transfer_deadline:
-        depositGroup.contractTermsRaw.wire_transfer_deadline,
-      refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
-      coin_sig: perm.coin_sig,
-      denom_pub_hash: perm.h_denom,
-      merchant_pub: depositGroup.merchantPub,
-      h_age_commitment: perm.h_age_commitment,
-    };
-    // Check for cancellation before making network request.
-    options.cancellationToken?.throwIfCancelled();
-    const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);
-    logger.info(`depositing to ${url}`);
-    const httpResp = await ws.http.postJson(url.href, requestBody, {
-      cancellationToken: options.cancellationToken,
-    });
-    await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
-    await ws.db
-      .mktx((x) => [x.depositGroups])
-      .runReadWrite(async (tx) => {
-        const dg = await tx.depositGroups.get(depositGroupId);
-        if (!dg) {
-          return;
-        }
-        dg.depositedPerCoin[i] = true;
-        await tx.depositGroups.put(dg);
+
+    let updatedDeposit: boolean | undefined = undefined;
+    let updatedTxStatus: TransactionStatus | undefined = undefined;
+
+    if (!depositGroup.depositedPerCoin[i]) {
+      const requestBody: ExchangeDepositRequest = {
+        contribution: Amounts.stringify(perm.contribution),
+        merchant_payto_uri: depositGroup.wire.payto_uri,
+        wire_salt: depositGroup.wire.salt,
+        h_contract_terms: depositGroup.contractTermsHash,
+        ub_sig: perm.ub_sig,
+        timestamp: depositGroup.contractTermsRaw.timestamp,
+        wire_transfer_deadline:
+          depositGroup.contractTermsRaw.wire_transfer_deadline,
+        refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
+        coin_sig: perm.coin_sig,
+        denom_pub_hash: perm.h_denom,
+        merchant_pub: depositGroup.merchantPub,
+        h_age_commitment: perm.h_age_commitment,
+      };
+      // Check for cancellation before making network request.
+      options.cancellationToken?.throwIfCancelled();
+      const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);
+      logger.info(`depositing to ${url}`);
+      const httpResp = await ws.http.postJson(url.href, requestBody, {
+        cancellationToken: options.cancellationToken,
       });
+      await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
+      updatedDeposit = true;
+    }
+
+    if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
+      const track = await trackDepositPermission(ws, depositGroup, perm);
+      updatedTxStatus = txStatusFromTrack(track);
+    }
+
+    if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
+      await ws.db
+        .mktx((x) => [x.depositGroups])
+        .runReadWrite(async (tx) => {
+          const dg = await tx.depositGroups.get(depositGroupId);
+          if (!dg) {
+            return;
+          }
+          if (updatedDeposit !== undefined) {
+            dg.depositedPerCoin[i] = updatedDeposit;
+          }
+          if (updatedTxStatus !== undefined) {
+            dg.transactionPerCoin[i] = updatedTxStatus;
+          }
+          await tx.depositGroups.put(dg);
+        });
+    }
   }
 
   await ws.db
@@ -157,13 +180,17 @@ export async function processDepositGroup(
       if (!dg) {
         return;
       }
-      let allDeposited = true;
-      for (const d of depositGroup.depositedPerCoin) {
-        if (!d) {
-          allDeposited = false;
+      let allDepositedAndWired = true;
+      for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
+        if (
+          !depositGroup.depositedPerCoin[i] ||
+          depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired
+        ) {
+          allDepositedAndWired = false;
+          break;
         }
       }
-      if (allDeposited) {
+      if (allDepositedAndWired) {
         dg.timestampFinished = TalerProtocolTimestamp.now();
         dg.operationStatus = OperationStatus.Finished;
         await tx.depositGroups.put(dg);
@@ -172,14 +199,24 @@ export async function processDepositGroup(
   return OperationAttemptResult.finishedEmpty();
 }
 
+function txStatusFromTrack(t: TrackTransaction): TransactionStatus {
+  if (t.type === "accepted") {
+    if (!t.kyc_ok && t.requirement_row !== undefined) {
+      return TransactionStatus.KycRequired;
+    }
+    return TransactionStatus.Accepted;
+  }
+  if (t.type === "wired") {
+    return TransactionStatus.Wired;
+  }
+  return TransactionStatus.Unknown;
+}
+
 export async function trackDepositGroup(
   ws: InternalWalletState,
   req: TrackDepositGroupRequest,
 ): Promise<TrackDepositGroupResponse> {
-  const responses: {
-    status: number;
-    body: any;
-  }[] = [];
+  const responses: TrackTransaction[] = [];
   const depositGroup = await ws.db
     .mktx((x) => [x.depositGroups])
     .runReadOnly(async (tx) => {
@@ -200,31 +237,55 @@ export async function trackDepositGroup(
     contractData,
   );
 
+  for (const dp of depositPermissions) {
+    const track = await trackDepositPermission(ws, depositGroup, dp);
+    responses.push(track);
+  }
+
+  return { responses };
+}
+
+async function trackDepositPermission(
+  ws: InternalWalletState,
+  depositGroup: DepositGroupRecord,
+  dp: CoinDepositPermission,
+): Promise<TrackTransaction> {
   const wireHash = depositGroup.contractTermsRaw.h_wire;
 
-  for (const dp of depositPermissions) {
-    const url = new URL(
-      
`deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
-      dp.exchange_url,
-    );
-    const sigResp = await ws.cryptoApi.signTrackTransaction({
-      coinPub: dp.coin_pub,
-      contractTermsHash: depositGroup.contractTermsHash,
-      merchantPriv: depositGroup.merchantPriv,
-      merchantPub: depositGroup.merchantPub,
-      wireHash,
-    });
-    url.searchParams.set("merchant_sig", sigResp.sig);
-    const httpResp = await ws.http.get(url.href);
-    const body = await httpResp.json();
-    responses.push({
-      body,
-      status: httpResp.status,
-    });
+  const url = new URL(
+    
`deposits/${wireHash}/${depositGroup.merchantPub}/${depositGroup.contractTermsHash}/${dp.coin_pub}`,
+    dp.exchange_url,
+  );
+  const sigResp = await ws.cryptoApi.signTrackTransaction({
+    coinPub: dp.coin_pub,
+    contractTermsHash: depositGroup.contractTermsHash,
+    merchantPriv: depositGroup.merchantPriv,
+    merchantPub: depositGroup.merchantPub,
+    wireHash,
+  });
+  url.searchParams.set("merchant_sig", sigResp.sig);
+  const httpResp = await ws.http.get(url.href);
+  switch (httpResp.status) {
+    case HttpStatusCode.Accepted: {
+      const accepted = await readSuccessResponseJsonOrThrow(
+        httpResp,
+        codecForTackTransactionAccepted(),
+      );
+      return { type: "accepted", ...accepted };
+    }
+    case HttpStatusCode.Ok: {
+      const wired = await readSuccessResponseJsonOrThrow(
+        httpResp,
+        codecForTackTransactionWired(),
+      );
+      return { type: "wired", ...wired };
+    }
+    default: {
+      throw Error(
+        `unexpected response from track-transaction (${httpResp.status})`,
+      );
+    }
   }
-  return {
-    responses,
-  };
 }
 
 export async function getFeeForDeposit(
@@ -491,6 +552,9 @@ export async function createDepositGroup(
     noncePub: noncePair.pub,
     timestampCreated: AbsoluteTime.toTimestamp(now),
     timestampFinished: undefined,
+    transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
+      () => TransactionStatus.Unknown,
+    ),
     payCoinSelection: payCoinSel.coinSel,
     payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
     depositedPerCoin: payCoinSel.coinSel.coinPubs.map(() => false),
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index a702fab2f..0e86c77ed 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -53,6 +53,7 @@ import {
   WalletContractData,
   PeerPushPaymentInitiationStatus,
   PeerPullPaymentIncomingStatus,
+  TransactionStatus,
 } from "../db.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
 import { assertUnreachable } from "../util/assertUnreachable.js";
@@ -552,6 +553,13 @@ function buildTransactionForDeposit(
       TransactionType.Deposit,
       dg.depositGroupId,
     ),
+    wireTransferProgress:
+      (100 *
+        dg.transactionPerCoin.reduce(
+          (prev, cur) => prev + (cur === TransactionStatus.Wired ? 1 : 0),
+          0,
+        )) /
+      dg.transactionPerCoin.length,
     depositGroupId: dg.depositGroupId,
     ...(ort?.lastError ? { error: ort.lastError } : {}),
   };

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