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: store total p2p


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: store total p2p push cost in DB
Date: Fri, 13 Jan 2023 02:24:21 +0100

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 a31b8c3c3 wallet-core: store total p2p push cost in DB
a31b8c3c3 is described below

commit a31b8c3c3105d0ba11f2a1c513c6b6bec3ebeb49
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri Jan 13 02:24:19 2023 +0100

    wallet-core: store total p2p push cost in DB
---
 packages/taler-wallet-core/src/db.ts               |   2 +
 .../taler-wallet-core/src/operations/pay-peer.ts   | 389 ++++++++++-----------
 .../src/operations/transactions.ts                 |   2 +-
 3 files changed, 196 insertions(+), 197 deletions(-)

diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 9ca88d086..adf704bc4 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1713,6 +1713,8 @@ export interface PeerPushPaymentInitiationRecord {
    */
   amount: AmountString;
 
+  totalCost: AmountString;
+
   coinSel: PeerPushPaymentCoinSelection;
 
   contractTermsHash: HashCodeString;
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts 
b/packages/taler-wallet-core/src/operations/pay-peer.ts
index 8dc468ad9..bb36217d6 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -103,20 +103,22 @@ import { internalCreateWithdrawalGroup } from 
"./withdraw.js";
 
 const logger = new Logger("operations/peer-to-peer.ts");
 
-export interface PeerCoinSelectionDetails {
+interface SelectedPeerCoin {
+  coinPub: string;
+  coinPriv: string;
+  contribution: AmountString;
+  denomPubHash: string;
+  denomSig: UnblindedSignature;
+  ageCommitmentProof: AgeCommitmentProof | undefined;
+}
+
+interface PeerCoinSelectionDetails {
   exchangeBaseUrl: string;
 
   /**
    * Info of Coins that were selected.
    */
-  coins: {
-    coinPub: string;
-    coinPriv: string;
-    contribution: AmountString;
-    denomPubHash: string;
-    denomSig: UnblindedSignature;
-    ageCommitmentProof: AgeCommitmentProof | undefined;
-  }[];
+  coins: SelectedPeerCoin[];
 
   /**
    * How much of the deposit fees is the customer paying?
@@ -195,152 +197,158 @@ export async function queryCoinInfosForSelection(
 
 export async function selectPeerCoins(
   ws: InternalWalletState,
-  tx: GetReadOnlyAccess<{
-    exchanges: typeof WalletStoresV1.exchanges;
-    denominations: typeof WalletStoresV1.denominations;
-    coins: typeof WalletStoresV1.coins;
-    coinAvailability: typeof WalletStoresV1.coinAvailability;
-    refreshGroups: typeof WalletStoresV1.refreshGroups;
-  }>,
   instructedAmount: AmountJson,
 ): Promise<SelectPeerCoinsResult> {
-  const exchanges = await tx.exchanges.iter().toArray();
-  const exchangeFeeGap: { [url: string]: AmountJson } = {};
-  const currency = Amounts.currencyOf(instructedAmount);
-  for (const exch of exchanges) {
-    if (exch.detailsPointer?.currency !== currency) {
-      continue;
-    }
-    const coins = (
-      await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
-    ).filter((x) => x.status === CoinStatus.Fresh);
-    const coinInfos: CoinInfo[] = [];
-    for (const coin of coins) {
-      const denom = await ws.getDenomInfo(
-        ws,
-        tx,
-        coin.exchangeBaseUrl,
-        coin.denomPubHash,
-      );
-      if (!denom) {
-        throw Error("denom not found");
-      }
-      coinInfos.push({
-        coinPub: coin.coinPub,
-        feeDeposit: Amounts.parseOrThrow(denom.feeDeposit),
-        value: Amounts.parseOrThrow(denom.value),
-        denomPubHash: denom.denomPubHash,
-        coinPriv: coin.coinPriv,
-        denomSig: coin.denomSig,
-        maxAge: coin.maxAge,
-        ageCommitmentProof: coin.ageCommitmentProof,
-      });
-    }
-    if (coinInfos.length === 0) {
-      continue;
-    }
-    coinInfos.sort(
-      (o1, o2) =>
-        -Amounts.cmp(o1.value, o2.value) ||
-        strcmp(o1.denomPubHash, o2.denomPubHash),
-    );
-    let amountAcc = Amounts.zeroOfCurrency(currency);
-    let depositFeesAcc = Amounts.zeroOfCurrency(currency);
-    const resCoins: {
-      coinPub: string;
-      coinPriv: string;
-      contribution: AmountString;
-      denomPubHash: string;
-      denomSig: UnblindedSignature;
-      ageCommitmentProof: AgeCommitmentProof | undefined;
-    }[] = [];
-    let lastDepositFee = Amounts.zeroOfCurrency(currency);
-    for (const coin of coinInfos) {
-      if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
-        break;
+  return await ws.db
+    .mktx((x) => [
+      x.exchanges,
+      x.contractTerms,
+      x.coins,
+      x.coinAvailability,
+      x.denominations,
+      x.refreshGroups,
+      x.peerPushPaymentInitiations,
+    ])
+    .runReadWrite(async (tx) => {
+      const exchanges = await tx.exchanges.iter().toArray();
+      const exchangeFeeGap: { [url: string]: AmountJson } = {};
+      const currency = Amounts.currencyOf(instructedAmount);
+      for (const exch of exchanges) {
+        if (exch.detailsPointer?.currency !== currency) {
+          continue;
+        }
+        const coins = (
+          await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
+        ).filter((x) => x.status === CoinStatus.Fresh);
+        const coinInfos: CoinInfo[] = [];
+        for (const coin of coins) {
+          const denom = await ws.getDenomInfo(
+            ws,
+            tx,
+            coin.exchangeBaseUrl,
+            coin.denomPubHash,
+          );
+          if (!denom) {
+            throw Error("denom not found");
+          }
+          coinInfos.push({
+            coinPub: coin.coinPub,
+            feeDeposit: Amounts.parseOrThrow(denom.feeDeposit),
+            value: Amounts.parseOrThrow(denom.value),
+            denomPubHash: denom.denomPubHash,
+            coinPriv: coin.coinPriv,
+            denomSig: coin.denomSig,
+            maxAge: coin.maxAge,
+            ageCommitmentProof: coin.ageCommitmentProof,
+          });
+        }
+        if (coinInfos.length === 0) {
+          continue;
+        }
+        coinInfos.sort(
+          (o1, o2) =>
+            -Amounts.cmp(o1.value, o2.value) ||
+            strcmp(o1.denomPubHash, o2.denomPubHash),
+        );
+        let amountAcc = Amounts.zeroOfCurrency(currency);
+        let depositFeesAcc = Amounts.zeroOfCurrency(currency);
+        const resCoins: {
+          coinPub: string;
+          coinPriv: string;
+          contribution: AmountString;
+          denomPubHash: string;
+          denomSig: UnblindedSignature;
+          ageCommitmentProof: AgeCommitmentProof | undefined;
+        }[] = [];
+        let lastDepositFee = Amounts.zeroOfCurrency(currency);
+        for (const coin of coinInfos) {
+          if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
+            break;
+          }
+          const gap = Amounts.add(
+            coin.feeDeposit,
+            Amounts.sub(instructedAmount, amountAcc).amount,
+          ).amount;
+          const contrib = Amounts.min(gap, coin.value);
+          amountAcc = Amounts.add(
+            amountAcc,
+            Amounts.sub(contrib, coin.feeDeposit).amount,
+          ).amount;
+          depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
+          resCoins.push({
+            coinPriv: coin.coinPriv,
+            coinPub: coin.coinPub,
+            contribution: Amounts.stringify(contrib),
+            denomPubHash: coin.denomPubHash,
+            denomSig: coin.denomSig,
+            ageCommitmentProof: coin.ageCommitmentProof,
+          });
+          lastDepositFee = coin.feeDeposit;
+        }
+        if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
+          const res: PeerCoinSelectionDetails = {
+            exchangeBaseUrl: exch.baseUrl,
+            coins: resCoins,
+            depositFees: depositFeesAcc,
+          };
+          return { type: "success", result: res };
+        }
+        const diff = Amounts.sub(instructedAmount, amountAcc).amount;
+        exchangeFeeGap[exch.baseUrl] = Amounts.add(lastDepositFee, 
diff).amount;
+
+        continue;
       }
-      const gap = Amounts.add(
-        coin.feeDeposit,
-        Amounts.sub(instructedAmount, amountAcc).amount,
-      ).amount;
-      const contrib = Amounts.min(gap, coin.value);
-      amountAcc = Amounts.add(
-        amountAcc,
-        Amounts.sub(contrib, coin.feeDeposit).amount,
-      ).amount;
-      depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
-      resCoins.push({
-        coinPriv: coin.coinPriv,
-        coinPub: coin.coinPub,
-        contribution: Amounts.stringify(contrib),
-        denomPubHash: coin.denomPubHash,
-        denomSig: coin.denomSig,
-        ageCommitmentProof: coin.ageCommitmentProof,
+      // We were unable to select coins.
+      // Now we need to produce error details.
+
+      const infoGeneral = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
+        currency,
       });
-      lastDepositFee = coin.feeDeposit;
-    }
-    if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
-      const res: PeerCoinSelectionDetails = {
-        exchangeBaseUrl: exch.baseUrl,
-        coins: resCoins,
-        depositFees: depositFeesAcc,
-      };
-      return { type: "success", result: res };
-    }
-    const diff = Amounts.sub(instructedAmount, amountAcc).amount;
-    exchangeFeeGap[exch.baseUrl] = Amounts.add(lastDepositFee, diff).amount;
 
-    continue;
-  }
-  // We were unable to select coins.
-  // Now we need to produce error details.
+      const perExchange: PayPeerInsufficientBalanceDetails["perExchange"] = {};
 
-  const infoGeneral = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
-    currency,
-  });
+      for (const exch of exchanges) {
+        if (exch.detailsPointer?.currency !== currency) {
+          continue;
+        }
+        const infoExchange = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
+          currency,
+          restrictExchangeTo: exch.baseUrl,
+        });
+        let gap =
+          exchangeFeeGap[exch.baseUrl] ?? Amounts.zeroOfCurrency(currency);
+        if (Amounts.cmp(infoExchange.balanceMaterial, instructedAmount) < 0) {
+          // Show fee gap only if we should've been able to pay with the 
material amount
+          gap = Amounts.zeroOfAmount(currency);
+        }
+        perExchange[exch.baseUrl] = {
+          balanceAvailable: Amounts.stringify(infoExchange.balanceAvailable),
+          balanceMaterial: Amounts.stringify(infoExchange.balanceMaterial),
+          feeGapEstimate: Amounts.stringify(gap),
+        };
+      }
 
-  const perExchange: PayPeerInsufficientBalanceDetails["perExchange"] = {};
+      const errDetails: PayPeerInsufficientBalanceDetails = {
+        amountRequested: Amounts.stringify(instructedAmount),
+        balanceAvailable: Amounts.stringify(infoGeneral.balanceAvailable),
+        balanceMaterial: Amounts.stringify(infoGeneral.balanceMaterial),
+        perExchange,
+      };
 
-  for (const exch of exchanges) {
-    if (exch.detailsPointer?.currency !== currency) {
-      continue;
-    }
-    const infoExchange = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
-      currency,
-      restrictExchangeTo: exch.baseUrl,
+      return { type: "failure", insufficientBalanceDetails: errDetails };
     });
-    let gap = exchangeFeeGap[exch.baseUrl] ?? Amounts.zeroOfCurrency(currency);
-    if (Amounts.cmp(infoExchange.balanceMaterial, instructedAmount) < 0) {
-      // Show fee gap only if we should've been able to pay with the material 
amount
-      gap = Amounts.zeroOfAmount(currency);
-    }
-    perExchange[exch.baseUrl] = {
-      balanceAvailable: Amounts.stringify(infoExchange.balanceAvailable),
-      balanceMaterial: Amounts.stringify(infoExchange.balanceMaterial),
-      feeGapEstimate: Amounts.stringify(gap),
-    };
-  }
-
-  const errDetails: PayPeerInsufficientBalanceDetails = {
-    amountRequested: Amounts.stringify(instructedAmount),
-    balanceAvailable: Amounts.stringify(infoGeneral.balanceAvailable),
-    balanceMaterial: Amounts.stringify(infoGeneral.balanceMaterial),
-    perExchange,
-  };
-
-  return { type: "failure", insufficientBalanceDetails: errDetails };
 }
 
 export async function getTotalPeerPaymentCost(
   ws: InternalWalletState,
-  pcs: PeerCoinSelectionDetails,
+  pcs: SelectedPeerCoin[],
 ): Promise<AmountJson> {
   return ws.db
     .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
       const costs: AmountJson[] = [];
-      for (let i = 0; i < pcs.coins.length; i++) {
-        const coin = await tx.coins.get(pcs.coins[i].coinPub);
+      for (let i = 0; i < pcs.length; i++) {
+        const coin = await tx.coins.get(pcs[i].coinPub);
         if (!coin) {
           throw Error("can't calculate payment cost, coin not found");
         }
@@ -358,22 +366,22 @@ export async function getTotalPeerPaymentCost(
           .filter((x) =>
             Amounts.isSameCurrency(
               DenominationRecord.getValue(x),
-              pcs.coins[i].contribution,
+              pcs[i].contribution,
             ),
           );
         const amountLeft = Amounts.sub(
           DenominationRecord.getValue(denom),
-          pcs.coins[i].contribution,
+          pcs[i].contribution,
         ).amount;
         const refreshCost = getTotalRefreshCost(
           allDenoms,
           DenominationRecord.toDenomInfo(denom),
           amountLeft,
         );
-        costs.push(Amounts.parseOrThrow(pcs.coins[i].contribution));
+        costs.push(Amounts.parseOrThrow(pcs[i].contribution));
         costs.push(refreshCost);
       }
-      const zero = Amounts.zeroOfAmount(pcs.coins[0].contribution);
+      const zero = Amounts.zeroOfAmount(pcs[0].contribution);
       return Amounts.sum([zero, ...costs]).amount;
     });
 }
@@ -383,20 +391,7 @@ export async function preparePeerPushPayment(
   req: PreparePeerPushPaymentRequest,
 ): Promise<PreparePeerPushPaymentResponse> {
   const instructedAmount = Amounts.parseOrThrow(req.amount);
-  const coinSelRes: SelectPeerCoinsResult = await ws.db
-    .mktx((x) => [
-      x.exchanges,
-      x.contractTerms,
-      x.coins,
-      x.coinAvailability,
-      x.denominations,
-      x.refreshGroups,
-      x.peerPushPaymentInitiations,
-    ])
-    .runReadWrite(async (tx) => {
-      const selRes = await selectPeerCoins(ws, tx, instructedAmount);
-      return selRes;
-    });
+  const coinSelRes = await selectPeerCoins(ws, instructedAmount);
   if (coinSelRes.type === "failure") {
     throw TalerError.fromDetail(
       TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
@@ -405,7 +400,10 @@ export async function preparePeerPushPayment(
       },
     );
   }
-  const totalAmount = await getTotalPeerPaymentCost(ws, coinSelRes.result);
+  const totalAmount = await getTotalPeerPaymentCost(
+    ws,
+    coinSelRes.result.coins,
+  );
   return {
     amountEffective: Amounts.stringify(totalAmount),
     amountRaw: req.amount,
@@ -517,7 +515,28 @@ export async function initiatePeerPushPayment(
   const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
 
   const contractKeyPair = await ws.cryptoApi.createEddsaKeypair({});
-  const coinSelRes: SelectPeerCoinsResult = await ws.db
+
+  const coinSelRes = await selectPeerCoins(ws, instructedAmount);
+
+  if (coinSelRes.type !== "success") {
+    throw TalerError.fromDetail(
+      TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
+      {
+        insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
+      },
+    );
+  }
+
+  const sel = coinSelRes.result;
+
+  logger.info(`selected p2p coins (push): ${j2s(coinSelRes)}`);
+
+  const totalAmount = await getTotalPeerPaymentCost(
+    ws,
+    coinSelRes.result.coins,
+  );
+
+  await ws.db
     .mktx((x) => [
       x.exchanges,
       x.contractTerms,
@@ -528,13 +547,6 @@ export async function initiatePeerPushPayment(
       x.peerPushPaymentInitiations,
     ])
     .runReadWrite(async (tx) => {
-      const selRes = await selectPeerCoins(ws, tx, instructedAmount);
-      if (selRes.type === "failure") {
-        return selRes;
-      }
-
-      const sel = selRes.result;
-
       await spendCoins(ws, tx, {
         allocationId: `txn:peer-push-debit:${pursePair.pub}`,
         coinPubs: sel.coins.map((x) => x.coinPub),
@@ -562,25 +574,14 @@ export async function initiatePeerPushPayment(
           coinPubs: sel.coins.map((x) => x.coinPub),
           contributions: sel.coins.map((x) => x.contribution),
         },
+        totalCost: Amounts.stringify(totalAmount),
       });
 
       await tx.contractTerms.put({
         h: hContractTerms,
         contractTermsRaw: contractTerms,
       });
-
-      return selRes;
     });
-  logger.info(`selected p2p coins (push): ${j2s(coinSelRes)}`);
-
-  if (coinSelRes.type !== "success") {
-    throw TalerError.fromDetail(
-      TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
-      {
-        insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
-      },
-    );
-  }
 
   await runOperationWithErrorReporting(
     ws,
@@ -866,7 +867,22 @@ export async function acceptPeerPullPayment(
   const instructedAmount = Amounts.parseOrThrow(
     peerPullInc.contractTerms.amount,
   );
-  const coinSelRes: SelectPeerCoinsResult = await ws.db
+
+  const coinSelRes = await selectPeerCoins(ws, instructedAmount);
+  logger.info(`selected p2p coins (pull): ${j2s(coinSelRes)}`);
+
+  if (coinSelRes.type !== "success") {
+    throw TalerError.fromDetail(
+      TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
+      {
+        insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
+      },
+    );
+  }
+
+  const sel = coinSelRes.result;
+
+  await ws.db
     .mktx((x) => [
       x.exchanges,
       x.coins,
@@ -876,13 +892,6 @@ export async function acceptPeerPullPayment(
       x.coinAvailability,
     ])
     .runReadWrite(async (tx) => {
-      const selRes = await selectPeerCoins(ws, tx, instructedAmount);
-      if (selRes.type !== "success") {
-        return selRes;
-      }
-
-      const sel = selRes.result;
-
       await spendCoins(ws, tx, {
         allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`,
         coinPubs: sel.coins.map((x) => x.coinPub),
@@ -900,19 +909,7 @@ export async function acceptPeerPullPayment(
       }
       pi.status = PeerPullPaymentIncomingStatus.Accepted;
       await tx.peerPullPaymentIncoming.put(pi);
-
-      return selRes;
     });
-  logger.info(`selected p2p coins (pull): ${j2s(coinSelRes)}`);
-
-  if (coinSelRes.type !== "success") {
-    throw TalerError.fromDetail(
-      TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
-      {
-        insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
-      },
-    );
-  }
 
   const pursePub = peerPullInc.pursePub;
 
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts 
b/packages/taler-wallet-core/src/operations/transactions.ts
index 43ee97239..80dc50eb8 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -346,7 +346,7 @@ function buildTransactionForPushPaymentDebit(
 ): Transaction {
   return {
     type: TransactionType.PeerPushDebit,
-    amountEffective: pi.amount,
+    amountEffective: pi.totalCost,
     amountRaw: pi.amount,
     exchangeBaseUrl: pi.exchangeBaseUrl,
     info: {

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