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: p2p support for


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: p2p support for transactions list
Date: Wed, 24 Aug 2022 22:17:22 +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 a11ac575 wallet-core: p2p support for transactions list
a11ac575 is described below

commit a11ac57535b0375f152ce115ee541cb8aca98e84
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Aug 24 22:17:19 2022 +0200

    wallet-core: p2p support for transactions list
---
 packages/taler-util/src/transactionsTypes.ts       |  88 +++-
 .../src/integrationtests/test-peer-to-peer-pull.ts |  44 +-
 .../src/integrationtests/test-peer-to-peer-push.ts |  13 +
 packages/taler-wallet-core/src/db.ts               |  30 +-
 .../src/operations/peer-to-peer.ts                 |  87 ++--
 .../src/operations/transactions.ts                 | 530 ++++++++++++---------
 .../taler-wallet-core/src/operations/withdraw.ts   |   5 +
 7 files changed, 524 insertions(+), 273 deletions(-)

diff --git a/packages/taler-util/src/transactionsTypes.ts 
b/packages/taler-util/src/transactionsTypes.ts
index 645f0120..c4bd3d46 100644
--- a/packages/taler-util/src/transactionsTypes.ts
+++ b/packages/taler-util/src/transactionsTypes.ts
@@ -102,7 +102,11 @@ export type Transaction =
   | TransactionRefund
   | TransactionTip
   | TransactionRefresh
-  | TransactionDeposit;
+  | TransactionDeposit
+  | TransactionPeerPullCredit
+  | TransactionPeerPullDebit
+  | TransactionPeerPushCredit
+  | TransactionPeerPushDebit;
 
 export enum TransactionType {
   Withdrawal = "withdrawal",
@@ -111,6 +115,10 @@ export enum TransactionType {
   Refresh = "refresh",
   Tip = "tip",
   Deposit = "deposit",
+  PeerPushDebit = "peer-push-debit",
+  PeerPushCredit = "peer-push-credit",
+  PeerPullDebit = "peer-pull-debit",
+  PeerPullCredit = "peer-pull-credit",
 }
 
 export enum WithdrawalType {
@@ -179,6 +187,76 @@ export interface TransactionWithdrawal extends 
TransactionCommon {
   withdrawalDetails: WithdrawalDetails;
 }
 
+export interface TransactionPeerPullCredit extends TransactionCommon {
+  type: TransactionType.PeerPullCredit;
+
+  /**
+   * Exchange used.
+   */
+  exchangeBaseUrl: string;
+
+  /**
+   * Amount that got subtracted from the reserve balance.
+   */
+  amountRaw: AmountString;
+
+  /**
+   * Amount that actually was (or will be) added to the wallet's balance.
+   */
+  amountEffective: AmountString;
+}
+
+export interface TransactionPeerPullDebit extends TransactionCommon {
+  type: TransactionType.PeerPullDebit;
+
+  /**
+   * Exchange used.
+   */
+  exchangeBaseUrl: string;
+
+  amountRaw: AmountString;
+
+  amountEffective: AmountString;
+}
+
+export interface TransactionPeerPushDebit extends TransactionCommon {
+  type: TransactionType.PeerPushDebit;
+
+  /**
+   * Exchange used.
+   */
+  exchangeBaseUrl: string;
+
+  /**
+   * Amount that got subtracted from the reserve balance.
+   */
+  amountRaw: AmountString;
+
+  /**
+   * Amount that actually was (or will be) added to the wallet's balance.
+   */
+  amountEffective: AmountString;
+}
+
+export interface TransactionPeerPushCredit extends TransactionCommon {
+  type: TransactionType.PeerPushCredit;
+
+  /**
+   * Exchange used.
+   */
+  exchangeBaseUrl: string;
+
+  /**
+   * Amount that got subtracted from the reserve balance.
+   */
+  amountRaw: AmountString;
+
+  /**
+   * Amount that actually was (or will be) added to the wallet's balance.
+   */
+  amountEffective: AmountString;
+}
+
 export enum PaymentStatus {
   /**
    * Explicitly aborted after timeout / failure
@@ -311,10 +389,10 @@ export interface OrderShortInfo {
 }
 
 export interface RefundInfoShort {
-  transactionId: string,
-  timestamp: TalerProtocolTimestamp,
-  amountEffective: AmountString,
-  amountRaw: AmountString,
+  transactionId: string;
+  timestamp: TalerProtocolTimestamp;
+  amountEffective: AmountString;
+  amountRaw: AmountString;
 }
 
 export interface TransactionRefund extends TransactionCommon {
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
index 1be1563c..0c149d63 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-pull.ts
@@ -19,7 +19,7 @@
  */
 import { j2s } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState } from "../harness/harness.js";
+import { GlobalTestState, WalletCli } from "../harness/harness.js";
 import {
   createSimpleTestkudosEnvironment,
   withdrawViaBank,
@@ -31,16 +31,23 @@ import {
 export async function runPeerToPeerPullTest(t: GlobalTestState) {
   // Set up test environment
 
-  const { wallet, bank, exchange, merchant } =
-    await createSimpleTestkudosEnvironment(t);
+  const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment(
+    t,
+  );
 
   // Withdraw digital cash into the wallet.
-
-  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
-
-  await wallet.runUntilDone();
-
-  const resp = await wallet.client.call(
+  const wallet1 = new WalletCli(t, "w1");
+  const wallet2 = new WalletCli(t, "w2");
+  await withdrawViaBank(t, {
+    wallet: wallet2,
+    bank,
+    exchange,
+    amount: "TESTKUDOS:20",
+  });
+
+  await wallet1.runUntilDone();
+
+  const resp = await wallet1.client.call(
     WalletApiOperation.InitiatePeerPullPayment,
     {
       exchangeBaseUrl: exchange.baseUrl,
@@ -51,7 +58,7 @@ export async function runPeerToPeerPullTest(t: 
GlobalTestState) {
     },
   );
 
-  const checkResp = await wallet.client.call(
+  const checkResp = await wallet2.client.call(
     WalletApiOperation.CheckPeerPullPayment,
     {
       talerUri: resp.talerUri,
@@ -60,18 +67,27 @@ export async function runPeerToPeerPullTest(t: 
GlobalTestState) {
 
   console.log(`checkResp: ${j2s(checkResp)}`);
 
-  const acceptResp = await wallet.client.call(
+  const acceptResp = await wallet2.client.call(
     WalletApiOperation.AcceptPeerPullPayment,
     {
       peerPullPaymentIncomingId: checkResp.peerPullPaymentIncomingId,
     },
   );
 
-  const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+  await wallet1.runUntilDone();
+  await wallet2.runUntilDone();
 
-  console.log(`transactions: ${j2s(txs)}`);
+  const txn1 = await wallet1.client.call(
+    WalletApiOperation.GetTransactions,
+    {},
+  );
+  const txn2 = await wallet2.client.call(
+    WalletApiOperation.GetTransactions,
+    {},
+  );
 
-  await wallet.runUntilDone();
+  console.log(`txn1: ${j2s(txn1)}`);
+  console.log(`txn2: ${j2s(txn2)}`);
 }
 
 runPeerToPeerPullTest.suites = ["wallet"];
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts
index bf65731d..ebbe87ae 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-peer-to-peer-push.ts
@@ -17,6 +17,7 @@
 /**
  * Imports.
  */
+import { j2s } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState, WalletCli } from "../harness/harness.js";
 import {
@@ -78,6 +79,18 @@ export async function runPeerToPeerPushTest(t: 
GlobalTestState) {
 
   await wallet1.runUntilDone();
   await wallet2.runUntilDone();
+
+  const txn1 = await wallet1.client.call(
+    WalletApiOperation.GetTransactions,
+    {},
+  );
+  const txn2 = await wallet2.client.call(
+    WalletApiOperation.GetTransactions,
+    {},
+  );
+
+  console.log(`txn1: ${j2s(txn1)}`);
+  console.log(`txn2: ${j2s(txn2)}`);
 }
 
 runPeerToPeerPushTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 3d59ce0a..e6b4854d 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1219,6 +1219,13 @@ export interface DenomSelectionState {
   }[];
 }
 
+export const enum WithdrawalRecordType {
+  BankManual = "bank-manual",
+  BankIntegrated = "bank-integrated",
+  PeerPullCredit = "peer-pull-credit",
+  PeerPushCredit = "peer-push-credit",
+}
+
 /**
  * Group of withdrawal operations that need to be executed.
  * (Either for a normal withdrawal or from a tip.)
@@ -1232,6 +1239,8 @@ export interface WithdrawalGroupRecord {
    */
   withdrawalGroupId: string;
 
+  withdrawalType: WithdrawalRecordType;
+
   /**
    * Secret seed used to derive planchets.
    * Stored since planchets are created lazily.
@@ -1607,8 +1616,6 @@ export interface PeerPushPaymentInitiationRecord {
 
   contractPriv: string;
 
-  contractPub: string;
-
   purseExpiration: TalerProtocolTimestamp;
 
   /**
@@ -1681,7 +1688,11 @@ export interface PeerPullPaymentIncomingRecord {
 
   contractTerms: PeerContractTerms;
 
-  timestamp: TalerProtocolTimestamp;
+  timestampCreated: TalerProtocolTimestamp;
+
+  paid: boolean;
+
+  accepted: boolean;
 
   contractPriv: string;
 }
@@ -1878,9 +1889,18 @@ export const WalletStoresV1 = {
       ]),
     },
   ),
-  peerPullPaymentInitiation: describeStore(
+  peerPullPaymentInitiations: describeStore(
     describeContents<PeerPullPaymentInitiationRecord>(
-      "peerPushPaymentInitiation",
+      "peerPullPaymentInitiations",
+      {
+        keyPath: "pursePub",
+      },
+    ),
+    {},
+  ),
+  peerPushPaymentInitiations: describeStore(
+    describeContents<PeerPushPaymentInitiationRecord>(
+      "peerPushPaymentInitiations",
       {
         keyPath: "pursePub",
       },
diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts 
b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
index ddfaa082..d6d71720 100644
--- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts
+++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
@@ -65,6 +65,7 @@ import {
   MergeReserveInfo,
   ReserveRecordStatus,
   WalletStoresV1,
+  WithdrawalRecordType,
 } from "../db.js";
 import { readSuccessResponseJsonOrThrow } from "../util/http.js";
 import { InternalWalletState } from "../internal-wallet-state.js";
@@ -208,12 +209,39 @@ export async function initiatePeerToPeerPush(
 ): Promise<InitiatePeerPushPaymentResponse> {
   // FIXME: actually create a record for retries here!
   const instructedAmount = Amounts.parseOrThrow(req.amount);
+
+  const pursePair = await ws.cryptoApi.createEddsaKeypair({});
+  const mergePair = await ws.cryptoApi.createEddsaKeypair({});
+
+  const purseExpiration: TalerProtocolTimestamp = AbsoluteTime.toTimestamp(
+    AbsoluteTime.addDuration(
+      AbsoluteTime.now(),
+      Duration.fromSpec({ days: 2 }),
+    ),
+  );
+
+  const contractTerms = {
+    ...req.partialContractTerms,
+    purse_expiration: purseExpiration,
+    amount: req.amount,
+  };
+
+  const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
+
+  const econtractResp = await ws.cryptoApi.encryptContractForMerge({
+    contractTerms,
+    mergePriv: mergePair.priv,
+    pursePriv: pursePair.priv,
+    pursePub: pursePair.pub,
+  });
+
   const coinSelRes: PeerCoinSelection | undefined = await ws.db
     .mktx((x) => ({
       exchanges: x.exchanges,
       coins: x.coins,
       denominations: x.denominations,
       refreshGroups: x.refreshGroups,
+      peerPushPaymentInitiations: x.peerPushPaymentInitiations,
     }))
     .runReadWrite(async (tx) => {
       const sel = await selectPeerCoins(ws, tx, instructedAmount);
@@ -232,6 +260,20 @@ export async function initiatePeerToPeerPush(
         await tx.coins.put(coin);
       }
 
+      await tx.peerPushPaymentInitiations.add({
+        amount: Amounts.stringify(instructedAmount),
+        contractPriv: econtractResp.contractPriv,
+        exchangeBaseUrl: sel.exchangeBaseUrl,
+        mergePriv: mergePair.priv,
+        mergePub: mergePair.pub,
+        // FIXME: only set this later!
+        purseCreated: true,
+        purseExpiration: purseExpiration,
+        pursePriv: pursePair.priv,
+        pursePub: pursePair.pub,
+        timestampCreated: TalerProtocolTimestamp.now(),
+      });
+
       await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
 
       return sel;
@@ -242,24 +284,6 @@ export async function initiatePeerToPeerPush(
     throw Error("insufficient balance");
   }
 
-  const pursePair = await ws.cryptoApi.createEddsaKeypair({});
-  const mergePair = await ws.cryptoApi.createEddsaKeypair({});
-
-  const purseExpiration: TalerProtocolTimestamp = AbsoluteTime.toTimestamp(
-    AbsoluteTime.addDuration(
-      AbsoluteTime.now(),
-      Duration.fromSpec({ days: 2 }),
-    ),
-  );
-
-  const contractTerms = {
-    ...req.partialContractTerms,
-    purse_expiration: purseExpiration,
-    amount: req.amount,
-  };
-
-  const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
-
   const purseSigResp = await ws.cryptoApi.signPurseCreation({
     hContractTerms,
     mergePub: mergePair.pub,
@@ -280,13 +304,6 @@ export async function initiatePeerToPeerPush(
     coinSelRes.exchangeBaseUrl,
   );
 
-  const econtractResp = await ws.cryptoApi.encryptContractForMerge({
-    contractTerms,
-    mergePriv: mergePair.priv,
-    pursePriv: pursePair.priv,
-    pursePub: pursePair.pub,
-  });
-
   const httpResp = await ws.http.postJson(createPurseUrl.href, {
     amount: Amounts.stringify(instructedAmount),
     merge_pub: mergePair.pub,
@@ -517,6 +534,7 @@ export async function acceptPeerPushPayment(
 
   await internalCreateWithdrawalGroup(ws, {
     amount,
+    withdrawalType: WithdrawalRecordType.PeerPushCredit,
     exchangeBaseUrl: peerInc.exchangeBaseUrl,
     reserveStatus: ReserveRecordStatus.QueryingStatus,
     reserveKeyPair: {
@@ -554,6 +572,7 @@ export async function acceptPeerPullPayment(
       coins: x.coins,
       denominations: x.denominations,
       refreshGroups: x.refreshGroups,
+      peerPullPaymentIncoming: x.peerPullPaymentIncoming,
     }))
     .runReadWrite(async (tx) => {
       const sel = await selectPeerCoins(ws, tx, instructedAmount);
@@ -574,6 +593,15 @@ export async function acceptPeerPullPayment(
 
       await createRefreshGroup(ws, tx, pubs, RefreshReason.Pay);
 
+      const pi = await tx.peerPullPaymentIncoming.get(
+        req.peerPullPaymentIncomingId,
+      );
+      if (!pi) {
+        throw Error();
+      }
+      pi.accepted = true;
+      await tx.peerPullPaymentIncoming.put(pi);
+
       return sel;
     });
   logger.info(`selected p2p coins: ${j2s(coinSelRes)}`);
@@ -656,8 +684,10 @@ export async function checkPeerPullPayment(
         contractPriv: contractPriv,
         exchangeBaseUrl: exchangeBaseUrl,
         pursePub: pursePub,
-        timestamp: TalerProtocolTimestamp.now(),
+        timestampCreated: TalerProtocolTimestamp.now(),
         contractTerms: dec.contractTerms,
+        paid: false,
+        accepted: false,
       });
     });
 
@@ -672,6 +702,8 @@ export async function initiatePeerRequestForPay(
   ws: InternalWalletState,
   req: InitiatePeerPullPaymentRequest,
 ): Promise<InitiatePeerPullPaymentResponse> {
+  await updateExchangeFromUrl(ws, req.exchangeBaseUrl);
+
   const mergeReserveInfo = await getMergeReserveInfo(ws, {
     exchangeBaseUrl: req.exchangeBaseUrl,
   });
@@ -727,7 +759,7 @@ export async function initiatePeerRequestForPay(
 
   await ws.db
     .mktx((x) => ({
-      peerPullPaymentInitiation: x.peerPullPaymentInitiation,
+      peerPullPaymentInitiation: x.peerPullPaymentInitiations,
     }))
     .runReadWrite(async (tx) => {
       await tx.peerPullPaymentInitiation.put({
@@ -772,6 +804,7 @@ export async function initiatePeerRequestForPay(
 
   await internalCreateWithdrawalGroup(ws, {
     amount: Amounts.parseOrThrow(req.amount),
+    withdrawalType: WithdrawalRecordType.PeerPullCredit,
     exchangeBaseUrl: req.exchangeBaseUrl,
     reserveStatus: ReserveRecordStatus.QueryingStatus,
     reserveKeyPair: {
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index ec499420..62df996c 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -38,6 +38,7 @@ import {
   RefundState,
   ReserveRecordStatus,
   WalletRefundItem,
+  WithdrawalRecordType,
 } from "../db.js";
 import { processDepositGroup } from "./deposits.js";
 import { getExchangeDetails } from "./exchanges.js";
@@ -101,10 +102,14 @@ const txOrder: { [t in TransactionType]: number } = {
   [TransactionType.Withdrawal]: 1,
   [TransactionType.Tip]: 2,
   [TransactionType.Payment]: 3,
-  [TransactionType.Refund]: 4,
-  [TransactionType.Deposit]: 5,
-  [TransactionType.Refresh]: 6,
-  [TransactionType.Tip]: 7,
+  [TransactionType.PeerPullCredit]: 4,
+  [TransactionType.PeerPullDebit]: 5,
+  [TransactionType.PeerPushCredit]: 6,
+  [TransactionType.PeerPushDebit]: 7,
+  [TransactionType.Refund]: 8,
+  [TransactionType.Deposit]: 9,
+  [TransactionType.Refresh]: 10,
+  [TransactionType.Tip]: 11,
 };
 
 /**
@@ -131,267 +136,348 @@ export async function getTransactions(
       recoupGroups: x.recoupGroups,
       depositGroups: x.depositGroups,
       tombstones: x.tombstones,
+      peerPushPaymentInitiations: x.peerPushPaymentInitiations,
+      peerPullPaymentIncoming: x.peerPullPaymentIncoming,
     }))
-    .runReadOnly(
-      // Report withdrawals that are currently in progress.
-      async (tx) => {
-        tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
-          if (
-            shouldSkipCurrency(
-              transactionsRequest,
-              wsr.rawWithdrawalAmount.currency,
-            )
-          ) {
-            return;
-          }
+    .runReadOnly(async (tx) => {
+      tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
+        const amount = Amounts.parseOrThrow(pi.amount);
+        if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
+          return;
+        }
+        if (shouldSkipSearch(transactionsRequest, [])) {
+          return;
+        }
+        transactions.push({
+          type: TransactionType.PeerPushDebit,
+          amountEffective: pi.amount,
+          amountRaw: pi.amount,
+          exchangeBaseUrl: pi.exchangeBaseUrl,
+          frozen: false,
+          pending: !pi.purseCreated,
+          timestamp: pi.timestampCreated,
+          transactionId: makeEventId(
+            TransactionType.PeerPushDebit,
+            pi.pursePub,
+          ),
+        });
+      });
 
-          if (shouldSkipSearch(transactionsRequest, [])) {
-            return;
-          }
-          let withdrawalDetails: WithdrawalDetails;
-          if (wsr.bankInfo) {
-            withdrawalDetails = {
-              type: WithdrawalType.TalerBankIntegrationApi,
-              confirmed: wsr.bankInfo.timestampBankConfirmed ? true : false,
-              reservePub: wsr.reservePub,
-              bankConfirmationUrl: wsr.bankInfo.confirmUrl,
-            };
-          } else {
-            const exchangeDetails = await getExchangeDetails(
-              tx,
-              wsr.exchangeBaseUrl,
-            );
-            if (!exchangeDetails) {
-              // FIXME: report somehow
-              return;
-            }
-            withdrawalDetails = {
-              type: WithdrawalType.ManualTransfer,
-              reservePub: wsr.reservePub,
-              exchangePaytoUris:
-                exchangeDetails.wireInfo?.accounts.map((x) => 
`${x.payto_uri}?subject=${wsr.reservePub}`) ??
-                [],
-            };
-          }
+      tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => {
+        const amount = Amounts.parseOrThrow(pi.contractTerms.amount);
+        if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
+          return;
+        }
+        if (shouldSkipSearch(transactionsRequest, [])) {
+          return;
+        }
+        if (!pi.accepted) {
+          return;
+        }
+        transactions.push({
+          type: TransactionType.PeerPullDebit,
+          amountEffective: Amounts.stringify(amount),
+          amountRaw: Amounts.stringify(amount),
+          exchangeBaseUrl: pi.exchangeBaseUrl,
+          frozen: false,
+          pending: false,
+          timestamp: pi.timestampCreated,
+          transactionId: makeEventId(
+            TransactionType.PeerPullDebit,
+            pi.pursePub,
+          ),
+        });
+      });
 
+      tx.withdrawalGroups.iter().forEachAsync(async (wsr) => {
+        if (
+          shouldSkipCurrency(
+            transactionsRequest,
+            wsr.rawWithdrawalAmount.currency,
+          )
+        ) {
+          return;
+        }
+
+        if (shouldSkipSearch(transactionsRequest, [])) {
+          return;
+        }
+        let withdrawalDetails: WithdrawalDetails;
+        if (wsr.withdrawalType === WithdrawalRecordType.PeerPullCredit) {
           transactions.push({
-            type: TransactionType.Withdrawal,
+            type: TransactionType.PeerPullCredit,
             amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
             amountRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
-            withdrawalDetails,
             exchangeBaseUrl: wsr.exchangeBaseUrl,
             pending: !wsr.timestampFinish,
             timestamp: wsr.timestampStart,
             transactionId: makeEventId(
-              TransactionType.Withdrawal,
+              TransactionType.PeerPullCredit,
               wsr.withdrawalGroupId,
             ),
             frozen: false,
             ...(wsr.lastError ? { error: wsr.lastError } : {}),
           });
-        });
-
-        tx.depositGroups.iter().forEachAsync(async (dg) => {
-          const amount = Amounts.parseOrThrow(dg.contractTermsRaw.amount);
-          if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
-            return;
-          }
-
+          return;
+        } else if (wsr.withdrawalType === WithdrawalRecordType.PeerPushCredit) 
{
           transactions.push({
-            type: TransactionType.Deposit,
-            amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
-            amountEffective: Amounts.stringify(dg.totalPayCost),
-            pending: !dg.timestampFinished,
-            frozen: false,
-            timestamp: dg.timestampCreated,
-            targetPaytoUri: dg.wire.payto_uri,
+            type: TransactionType.PeerPushCredit,
+            amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
+            amountRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
+            exchangeBaseUrl: wsr.exchangeBaseUrl,
+            pending: !wsr.timestampFinish,
+            timestamp: wsr.timestampStart,
             transactionId: makeEventId(
-              TransactionType.Deposit,
-              dg.depositGroupId,
+              TransactionType.PeerPushCredit,
+              wsr.withdrawalGroupId,
             ),
-            depositGroupId: dg.depositGroupId,
-            ...(dg.lastError ? { error: dg.lastError } : {}),
+            frozen: false,
+            ...(wsr.lastError ? { error: wsr.lastError } : {}),
           });
-        });
-
-        tx.purchases.iter().forEachAsync(async (pr) => {
-          if (
-            shouldSkipCurrency(
-              transactionsRequest,
-              pr.download.contractData.amount.currency,
-            )
-          ) {
-            return;
-          }
-          const contractData = pr.download.contractData;
-          if (shouldSkipSearch(transactionsRequest, [contractData.summary])) {
-            return;
-          }
-          const proposal = await tx.proposals.get(pr.proposalId);
-          if (!proposal) {
+          return;
+        } else if (wsr.bankInfo) {
+          withdrawalDetails = {
+            type: WithdrawalType.TalerBankIntegrationApi,
+            confirmed: wsr.bankInfo.timestampBankConfirmed ? true : false,
+            reservePub: wsr.reservePub,
+            bankConfirmationUrl: wsr.bankInfo.confirmUrl,
+          };
+        } else {
+          const exchangeDetails = await getExchangeDetails(
+            tx,
+            wsr.exchangeBaseUrl,
+          );
+          if (!exchangeDetails) {
+            // FIXME: report somehow
             return;
           }
-          const info: OrderShortInfo = {
-            merchant: contractData.merchant,
-            orderId: contractData.orderId,
-            products: contractData.products,
-            summary: contractData.summary,
-            summary_i18n: contractData.summaryI18n,
-            contractTermsHash: contractData.contractTermsHash,
+          withdrawalDetails = {
+            type: WithdrawalType.ManualTransfer,
+            reservePub: wsr.reservePub,
+            exchangePaytoUris:
+              exchangeDetails.wireInfo?.accounts.map(
+                (x) => `${x.payto_uri}?subject=${wsr.reservePub}`,
+              ) ?? [],
           };
-          if (contractData.fulfillmentUrl !== "") {
-            info.fulfillmentUrl = contractData.fulfillmentUrl;
+        }
+
+        transactions.push({
+          type: TransactionType.Withdrawal,
+          amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
+          amountRaw: Amounts.stringify(wsr.rawWithdrawalAmount),
+          withdrawalDetails,
+          exchangeBaseUrl: wsr.exchangeBaseUrl,
+          pending: !wsr.timestampFinish,
+          timestamp: wsr.timestampStart,
+          transactionId: makeEventId(
+            TransactionType.Withdrawal,
+            wsr.withdrawalGroupId,
+          ),
+          frozen: false,
+          ...(wsr.lastError ? { error: wsr.lastError } : {}),
+        });
+      });
+
+      tx.depositGroups.iter().forEachAsync(async (dg) => {
+        const amount = Amounts.parseOrThrow(dg.contractTermsRaw.amount);
+        if (shouldSkipCurrency(transactionsRequest, amount.currency)) {
+          return;
+        }
+
+        transactions.push({
+          type: TransactionType.Deposit,
+          amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
+          amountEffective: Amounts.stringify(dg.totalPayCost),
+          pending: !dg.timestampFinished,
+          frozen: false,
+          timestamp: dg.timestampCreated,
+          targetPaytoUri: dg.wire.payto_uri,
+          transactionId: makeEventId(
+            TransactionType.Deposit,
+            dg.depositGroupId,
+          ),
+          depositGroupId: dg.depositGroupId,
+          ...(dg.lastError ? { error: dg.lastError } : {}),
+        });
+      });
+
+      tx.purchases.iter().forEachAsync(async (pr) => {
+        if (
+          shouldSkipCurrency(
+            transactionsRequest,
+            pr.download.contractData.amount.currency,
+          )
+        ) {
+          return;
+        }
+        const contractData = pr.download.contractData;
+        if (shouldSkipSearch(transactionsRequest, [contractData.summary])) {
+          return;
+        }
+        const proposal = await tx.proposals.get(pr.proposalId);
+        if (!proposal) {
+          return;
+        }
+        const info: OrderShortInfo = {
+          merchant: contractData.merchant,
+          orderId: contractData.orderId,
+          products: contractData.products,
+          summary: contractData.summary,
+          summary_i18n: contractData.summaryI18n,
+          contractTermsHash: contractData.contractTermsHash,
+        };
+        if (contractData.fulfillmentUrl !== "") {
+          info.fulfillmentUrl = contractData.fulfillmentUrl;
+        }
+        const paymentTransactionId = makeEventId(
+          TransactionType.Payment,
+          pr.proposalId,
+        );
+        const refundGroupKeys = new Set<string>();
+
+        for (const rk of Object.keys(pr.refunds)) {
+          const refund = pr.refunds[rk];
+          const groupKey = `${refund.executionTime.t_s}`;
+          refundGroupKeys.add(groupKey);
+        }
+
+        let totalRefundRaw = Amounts.getZero(contractData.amount.currency);
+        let totalRefundEffective = Amounts.getZero(
+          contractData.amount.currency,
+        );
+        const refunds: RefundInfoShort[] = [];
+
+        for (const groupKey of refundGroupKeys.values()) {
+          const refundTombstoneId = makeEventId(
+            TombstoneTag.DeleteRefund,
+            pr.proposalId,
+            groupKey,
+          );
+          const tombstone = await tx.tombstones.get(refundTombstoneId);
+          if (tombstone) {
+            continue;
           }
-          const paymentTransactionId = makeEventId(
-            TransactionType.Payment,
+          const refundTransactionId = makeEventId(
+            TransactionType.Refund,
             pr.proposalId,
+            groupKey,
           );
-          const refundGroupKeys = new Set<string>();
-
+          let r0: WalletRefundItem | undefined;
+          let amountRaw = Amounts.getZero(contractData.amount.currency);
+          let amountEffective = Amounts.getZero(contractData.amount.currency);
           for (const rk of Object.keys(pr.refunds)) {
             const refund = pr.refunds[rk];
-            const groupKey = `${refund.executionTime.t_s}`;
-            refundGroupKeys.add(groupKey);
-          }
-
-          let totalRefundRaw = Amounts.getZero(contractData.amount.currency);
-          let totalRefundEffective = Amounts.getZero(
-            contractData.amount.currency,
-          );
-          const refunds: RefundInfoShort[] = [];
-
-          for (const groupKey of refundGroupKeys.values()) {
-            const refundTombstoneId = makeEventId(
-              TombstoneTag.DeleteRefund,
-              pr.proposalId,
-              groupKey,
-            );
-            const tombstone = await tx.tombstones.get(refundTombstoneId);
-            if (tombstone) {
+            const myGroupKey = `${refund.executionTime.t_s}`;
+            if (myGroupKey !== groupKey) {
               continue;
             }
-            const refundTransactionId = makeEventId(
-              TransactionType.Refund,
-              pr.proposalId,
-              groupKey,
-            );
-            let r0: WalletRefundItem | undefined;
-            let amountRaw = Amounts.getZero(contractData.amount.currency);
-            let amountEffective = 
Amounts.getZero(contractData.amount.currency);
-            for (const rk of Object.keys(pr.refunds)) {
-              const refund = pr.refunds[rk];
-              const myGroupKey = `${refund.executionTime.t_s}`;
-              if (myGroupKey !== groupKey) {
-                continue;
-              }
-              if (!r0) {
-                r0 = refund;
-              }
-
-              if (refund.type === RefundState.Applied) {
-                amountRaw = Amounts.add(amountRaw, refund.refundAmount).amount;
-                amountEffective = Amounts.add(
-                  amountEffective,
-                  Amounts.sub(
-                    refund.refundAmount,
-                    refund.refundFee,
-                    refund.totalRefreshCostBound,
-                  ).amount,
-                ).amount;
-
-                refunds.push({
-                  transactionId: refundTransactionId,
-                  timestamp: r0.obtainedTime,
-                  amountEffective: Amounts.stringify(amountEffective),
-                  amountRaw: Amounts.stringify(amountRaw),
-                });
-              }
-            }
             if (!r0) {
-              throw Error("invariant violated");
+              r0 = refund;
             }
 
-            totalRefundRaw = Amounts.add(totalRefundRaw, amountRaw).amount;
-            totalRefundEffective = Amounts.add(
-              totalRefundEffective,
-              amountEffective,
-            ).amount;
-            transactions.push({
-              type: TransactionType.Refund,
-              info,
-              refundedTransactionId: paymentTransactionId,
-              transactionId: refundTransactionId,
-              timestamp: r0.obtainedTime,
-              amountEffective: Amounts.stringify(amountEffective),
-              amountRaw: Amounts.stringify(amountRaw),
-              refundPending:
-                pr.refundAwaiting === undefined
-                  ? undefined
-                  : Amounts.stringify(pr.refundAwaiting),
-              pending: false,
-              frozen: false,
-            });
+            if (refund.type === RefundState.Applied) {
+              amountRaw = Amounts.add(amountRaw, refund.refundAmount).amount;
+              amountEffective = Amounts.add(
+                amountEffective,
+                Amounts.sub(
+                  refund.refundAmount,
+                  refund.refundFee,
+                  refund.totalRefreshCostBound,
+                ).amount,
+              ).amount;
+
+              refunds.push({
+                transactionId: refundTransactionId,
+                timestamp: r0.obtainedTime,
+                amountEffective: Amounts.stringify(amountEffective),
+                amountRaw: Amounts.stringify(amountRaw),
+              });
+            }
+          }
+          if (!r0) {
+            throw Error("invariant violated");
           }
 
-          const err = pr.lastPayError ?? pr.lastRefundStatusError;
+          totalRefundRaw = Amounts.add(totalRefundRaw, amountRaw).amount;
+          totalRefundEffective = Amounts.add(
+            totalRefundEffective,
+            amountEffective,
+          ).amount;
           transactions.push({
-            type: TransactionType.Payment,
-            amountRaw: Amounts.stringify(contractData.amount),
-            amountEffective: Amounts.stringify(pr.totalPayCost),
-            totalRefundRaw: Amounts.stringify(totalRefundRaw),
-            totalRefundEffective: Amounts.stringify(totalRefundEffective),
+            type: TransactionType.Refund,
+            info,
+            refundedTransactionId: paymentTransactionId,
+            transactionId: refundTransactionId,
+            timestamp: r0.obtainedTime,
+            amountEffective: Amounts.stringify(amountEffective),
+            amountRaw: Amounts.stringify(amountRaw),
             refundPending:
               pr.refundAwaiting === undefined
                 ? undefined
                 : Amounts.stringify(pr.refundAwaiting),
-            status: pr.timestampFirstSuccessfulPay
-              ? PaymentStatus.Paid
-              : PaymentStatus.Accepted,
-            pending:
-              !pr.timestampFirstSuccessfulPay &&
-              pr.abortStatus === AbortStatus.None,
-            refunds,
-            timestamp: pr.timestampAccept,
-            transactionId: paymentTransactionId,
-            proposalId: pr.proposalId,
-            info,
-            frozen: pr.payFrozen ?? false,
-            ...(err ? { error: err } : {}),
+            pending: false,
+            frozen: false,
           });
+        }
+
+        const err = pr.lastPayError ?? pr.lastRefundStatusError;
+        transactions.push({
+          type: TransactionType.Payment,
+          amountRaw: Amounts.stringify(contractData.amount),
+          amountEffective: Amounts.stringify(pr.totalPayCost),
+          totalRefundRaw: Amounts.stringify(totalRefundRaw),
+          totalRefundEffective: Amounts.stringify(totalRefundEffective),
+          refundPending:
+            pr.refundAwaiting === undefined
+              ? undefined
+              : Amounts.stringify(pr.refundAwaiting),
+          status: pr.timestampFirstSuccessfulPay
+            ? PaymentStatus.Paid
+            : PaymentStatus.Accepted,
+          pending:
+            !pr.timestampFirstSuccessfulPay &&
+            pr.abortStatus === AbortStatus.None,
+          refunds,
+          timestamp: pr.timestampAccept,
+          transactionId: paymentTransactionId,
+          proposalId: pr.proposalId,
+          info,
+          frozen: pr.payFrozen ?? false,
+          ...(err ? { error: err } : {}),
         });
+      });
 
-        tx.tips.iter().forEachAsync(async (tipRecord) => {
-          if (
-            shouldSkipCurrency(
-              transactionsRequest,
-              tipRecord.tipAmountRaw.currency,
-            )
-          ) {
-            return;
-          }
-          if (!tipRecord.acceptedTimestamp) {
-            return;
-          }
-          transactions.push({
-            type: TransactionType.Tip,
-            amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
-            amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
-            pending: !tipRecord.pickedUpTimestamp,
-            frozen: false,
-            timestamp: tipRecord.acceptedTimestamp,
-            transactionId: makeEventId(
-              TransactionType.Tip,
-              tipRecord.walletTipId,
-            ),
-            merchantBaseUrl: tipRecord.merchantBaseUrl,
-            // merchant: {
-            //   name: tipRecord.merchantBaseUrl,
-            // },
-            error: tipRecord.lastError,
-          });
+      tx.tips.iter().forEachAsync(async (tipRecord) => {
+        if (
+          shouldSkipCurrency(
+            transactionsRequest,
+            tipRecord.tipAmountRaw.currency,
+          )
+        ) {
+          return;
+        }
+        if (!tipRecord.acceptedTimestamp) {
+          return;
+        }
+        transactions.push({
+          type: TransactionType.Tip,
+          amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
+          amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
+          pending: !tipRecord.pickedUpTimestamp,
+          frozen: false,
+          timestamp: tipRecord.acceptedTimestamp,
+          transactionId: makeEventId(
+            TransactionType.Tip,
+            tipRecord.walletTipId,
+          ),
+          merchantBaseUrl: tipRecord.merchantBaseUrl,
+          // merchant: {
+          //   name: tipRecord.merchantBaseUrl,
+          // },
+          error: tipRecord.lastError,
         });
-      },
-    );
+      });
+    });
 
   const txPending = transactions.filter((x) => x.pending);
   const txNotPending = transactions.filter((x) => !x.pending);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 3c4e2d98..4e350670 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -74,6 +74,7 @@ import {
   ReserveRecordStatus,
   WalletStoresV1,
   WithdrawalGroupRecord,
+  WithdrawalRecordType,
 } from "../db.js";
 import {
   getErrorDetailFromException,
@@ -1700,6 +1701,7 @@ export async function internalCreateWithdrawalGroup(
     forcedDenomSel?: ForcedDenomSel;
     reserveKeyPair?: EddsaKeypair;
     restrictAge?: number;
+    withdrawalType: WithdrawalRecordType;
   },
 ): Promise<WithdrawalGroupRecord> {
   const reserveKeyPair =
@@ -1745,6 +1747,7 @@ export async function internalCreateWithdrawalGroup(
     restrictAge: args.restrictAge,
     senderWire: undefined,
     timestampFinish: undefined,
+    withdrawalType: args.withdrawalType,
   };
 
   const exchangeInfo = await updateExchangeFromUrl(ws, canonExchange);
@@ -1819,6 +1822,7 @@ export async function acceptWithdrawalFromUri(
   const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
     amount: withdrawInfo.amount,
     exchangeBaseUrl: req.selectedExchange,
+    withdrawalType: WithdrawalRecordType.BankIntegrated,
     forcedDenomSel: req.forcedDenomSel,
     reserveStatus: ReserveRecordStatus.RegisteringBank,
     bankInfo: {
@@ -1877,6 +1881,7 @@ export async function createManualWithdrawal(
 ): Promise<AcceptManualWithdrawalResult> {
   const withdrawalGroup = await internalCreateWithdrawalGroup(ws, {
     amount: Amounts.jsonifyAmount(req.amount),
+    withdrawalType: WithdrawalRecordType.BankManual,
     exchangeBaseUrl: req.exchangeBaseUrl,
     bankInfo: undefined,
     forcedDenomSel: req.forcedDenomSel,

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