gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 02/02: wallet-core: allow failure result in peer pay


From: gnunet
Subject: [taler-wallet-core] 02/02: wallet-core: allow failure result in peer payment coin selection
Date: Fri, 06 Jan 2023 11:08:51 +0100

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

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

commit c2c35925bb953bf07e32c005dbe312d220b45749
Author: Florian Dold <florian@dold.me>
AuthorDate: Fri Jan 6 11:08:45 2023 +0100

    wallet-core: allow failure result in peer payment coin selection
---
 packages/taler-util/src/taler-error-codes.ts       |  8 +++
 packages/taler-util/src/wallet-types.ts            | 65 +++++++++++++++++-----
 .../taler-wallet-core/src/operations/pay-peer.ts   | 63 ++++++++++++---------
 3 files changed, 97 insertions(+), 39 deletions(-)

diff --git a/packages/taler-util/src/taler-error-codes.ts 
b/packages/taler-util/src/taler-error-codes.ts
index 8bac5bffb..5e3c8fdfb 100644
--- a/packages/taler-util/src/taler-error-codes.ts
+++ b/packages/taler-util/src/taler-error-codes.ts
@@ -3240,6 +3240,14 @@ export enum TalerErrorCode {
   WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE = 7026,
 
 
+  /**
+   * The wallet does not have sufficient balance to create a peer push payment.
+   * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE = 7027,
+
+
   /**
    * We encountered a timeout with our payment backend.
    * Returned with an HTTP status code of #MHD_HTTP_GATEWAY_TIMEOUT (504).
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index b63e043f5..3a1176021 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -419,7 +419,10 @@ export const codecForPreparePayResultInsufficientBalance =
         "status",
         codecForConstString(PreparePayResultType.InsufficientBalance),
       )
-      .property("balanceDetails", 
codecForPayMerchantInsufficientBalanceDetails())
+      .property(
+        "balanceDetails",
+        codecForPayMerchantInsufficientBalanceDetails(),
+      )
       .build("PreparePayResultInsufficientBalance");
 
 export const codecForPreparePayResultAlreadyConfirmed =
@@ -2084,7 +2087,6 @@ export interface InitiatePeerPullPaymentResponse {
   transactionId: string;
 }
 
-
 /**
  * Detailed reason for why the wallet's balance is insufficient.
  */
@@ -2124,23 +2126,58 @@ export interface PayMerchantInsufficientBalanceDetails {
    * (i.e. balanceMechantWireable >= amountRequested),
    * this field contains an estimate of the amount that would additionally
    * be required to cover the fees.
-   * 
+   *
    * It is not possible to give an exact value here, since it depends
    * on the coin selection for the amount that would be additionally withdrawn.
    */
   feeGapEstimate: AmountString;
 }
 
-const codecForPayMerchantInsufficientBalanceDetails = 
-(): Codec<PayMerchantInsufficientBalanceDetails> =>
-  buildCodecForObject<PayMerchantInsufficientBalanceDetails>()
-    .property("amountRequested", codecForAmountString())
-    .property("balanceAgeAcceptable", codecForAmountString())
-    .property("balanceAvailable", codecForAmountString())
-    .property("balanceMaterial", codecForAmountString())
-    .property("balanceMerchantAcceptable", codecForAmountString())
-    .property("balanceMerchantDepositable", codecForAmountString())
-    .property("feeGapEstimate", codecForAmountString())
-    .build("PayMerchantInsufficientBalanceDetails");
+export const codecForPayMerchantInsufficientBalanceDetails =
+  (): Codec<PayMerchantInsufficientBalanceDetails> =>
+    buildCodecForObject<PayMerchantInsufficientBalanceDetails>()
+      .property("amountRequested", codecForAmountString())
+      .property("balanceAgeAcceptable", codecForAmountString())
+      .property("balanceAvailable", codecForAmountString())
+      .property("balanceMaterial", codecForAmountString())
+      .property("balanceMerchantAcceptable", codecForAmountString())
+      .property("balanceMerchantDepositable", codecForAmountString())
+      .property("feeGapEstimate", codecForAmountString())
+      .build("PayMerchantInsufficientBalanceDetails");
+
+/**
+ * Detailed reason for why the wallet's balance is insufficient.
+ */
+export interface PayPeerInsufficientBalanceDetails {
+  /**
+   * Amount requested by the merchant.
+   */
+  amountRequested: AmountString;
+
+  /**
+   * Balance of type "available" (see balance.ts for definition).
+   */
+  balanceAvailable: AmountString;
+
+  /**
+   * Balance of type "material" (see balance.ts for definition).
+   */
+  balanceMaterial: AmountString;
 
+  /**
+   * Acceptable balance based on restrictions on which
+   * exchange can be used.
+   */
+  balanceExchangeAcceptable: AmountString
 
+  /**
+   * If the payment would succeed without fees
+   * (i.e. balanceExchangeAcceptable >= amountRequested),
+   * this field contains an estimate of the amount that would additionally
+   * be required to cover the fees.
+   *
+   * It is not possible to give an exact value here, since it depends
+   * on the coin selection for the amount that would be additionally withdrawn.
+   */
+  feeGapEstimate: AmountString;
+}
diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts 
b/packages/taler-wallet-core/src/operations/pay-peer.ts
index cc859f243..3d03c46db 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer.ts
@@ -18,7 +18,6 @@
  * Imports.
  */
 import {
-  AbsoluteTime,
   AcceptPeerPullPaymentRequest,
   AcceptPeerPullPaymentResponse,
   AcceptPeerPushPaymentRequest,
@@ -41,7 +40,6 @@ import {
   constructPayPushUri,
   ContractTermsUtil,
   decodeCrock,
-  Duration,
   eddsaGetPublic,
   encodeCrock,
   ExchangePurseDeposits,
@@ -56,6 +54,7 @@ import {
   Logger,
   parsePayPullUri,
   parsePayPushUri,
+  PayPeerInsufficientBalanceDetails,
   PeerContractTerms,
   PreparePeerPullPaymentRequest,
   PreparePeerPullPaymentResponse,
@@ -132,6 +131,12 @@ interface CoinInfo {
   ageCommitmentProof?: AgeCommitmentProof;
 }
 
+export type SelectPeerCoinsResult =
+  | { type: "success"; result: PeerCoinSelection }
+  | {
+      type: "failure";
+    };
+
 export async function selectPeerCoins(
   ws: InternalWalletState,
   tx: GetReadOnlyAccess<{
@@ -140,7 +145,7 @@ export async function selectPeerCoins(
     coins: typeof WalletStoresV1.coins;
   }>,
   instructedAmount: AmountJson,
-): Promise<PeerCoinSelection | undefined> {
+): Promise<SelectPeerCoinsResult> {
   const exchanges = await tx.exchanges.iter().toArray();
   for (const exch of exchanges) {
     if (exch.detailsPointer?.currency !== instructedAmount.currency) {
@@ -218,11 +223,11 @@ export async function selectPeerCoins(
         coins: resCoins,
         depositFees: depositFeesAcc,
       };
-      return res;
+      return { type: "success", result: res };
     }
     continue;
   }
-  return undefined;
+  return { type: "failure" };
 }
 
 export async function preparePeerPushPayment(
@@ -258,7 +263,7 @@ export async function initiatePeerToPeerPush(
     pursePub: pursePair.pub,
   });
 
-  const coinSelRes: PeerCoinSelection | undefined = await ws.db
+  const coinSelRes: SelectPeerCoinsResult = await ws.db
     .mktx((x) => [
       x.exchanges,
       x.contractTerms,
@@ -270,11 +275,13 @@ export async function initiatePeerToPeerPush(
       x.peerPushPaymentInitiations,
     ])
     .runReadWrite(async (tx) => {
-      const sel = await selectPeerCoins(ws, tx, instructedAmount);
-      if (!sel) {
-        return undefined;
+      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),
@@ -304,11 +311,12 @@ export async function initiatePeerToPeerPush(
         contractTermsRaw: contractTerms,
       });
 
-      return sel;
+      return selRes;
     });
   logger.info(`selected p2p coins (push): ${j2s(coinSelRes)}`);
 
-  if (!coinSelRes) {
+  if (coinSelRes.type !== "success") {
+    // FIXME: use error code with details here
     throw Error("insufficient balance");
   }
 
@@ -322,14 +330,14 @@ export async function initiatePeerToPeerPush(
   });
 
   const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
-    exchangeBaseUrl: coinSelRes.exchangeBaseUrl,
+    exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
     pursePub: pursePair.pub,
-    coins: coinSelRes.coins,
+    coins: coinSelRes.result.coins,
   });
 
   const createPurseUrl = new URL(
     `purses/${pursePair.pub}/create`,
-    coinSelRes.exchangeBaseUrl,
+    coinSelRes.result.exchangeBaseUrl,
   );
 
   const httpResp = await ws.http.postJson(createPurseUrl.href, {
@@ -355,9 +363,9 @@ export async function initiatePeerToPeerPush(
     contractPriv: econtractResp.contractPriv,
     mergePriv: mergePair.priv,
     pursePub: pursePair.pub,
-    exchangeBaseUrl: coinSelRes.exchangeBaseUrl,
+    exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
     talerUri: constructPayPushUri({
-      exchangeBaseUrl: coinSelRes.exchangeBaseUrl,
+      exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
       contractPriv: econtractResp.contractPriv,
     }),
     transactionId: makeTransactionId(
@@ -627,7 +635,7 @@ export async function acceptPeerPullPayment(
   const instructedAmount = Amounts.parseOrThrow(
     peerPullInc.contractTerms.amount,
   );
-  const coinSelRes: PeerCoinSelection | undefined = await ws.db
+  const coinSelRes: SelectPeerCoinsResult = await ws.db
     .mktx((x) => [
       x.exchanges,
       x.coins,
@@ -637,11 +645,13 @@ export async function acceptPeerPullPayment(
       x.coinAvailability,
     ])
     .runReadWrite(async (tx) => {
-      const sel = await selectPeerCoins(ws, tx, instructedAmount);
-      if (!sel) {
-        return undefined;
+      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),
@@ -660,25 +670,27 @@ export async function acceptPeerPullPayment(
       pi.status = PeerPullPaymentIncomingStatus.Accepted;
       await tx.peerPullPaymentIncoming.put(pi);
 
-      return sel;
+      return selRes;
     });
   logger.info(`selected p2p coins (pull): ${j2s(coinSelRes)}`);
 
-  if (!coinSelRes) {
+  if (coinSelRes.type !== "success") {
     throw Error("insufficient balance");
   }
 
   const pursePub = peerPullInc.pursePub;
 
+  const coinSel = coinSelRes.result;
+
   const depositSigsResp = await ws.cryptoApi.signPurseDeposits({
-    exchangeBaseUrl: coinSelRes.exchangeBaseUrl,
+    exchangeBaseUrl: coinSel.exchangeBaseUrl,
     pursePub,
-    coins: coinSelRes.coins,
+    coins: coinSel.coins,
   });
 
   const purseDepositUrl = new URL(
     `purses/${pursePub}/deposit`,
-    coinSelRes.exchangeBaseUrl,
+    coinSel.exchangeBaseUrl,
   );
 
   const depositPayload: ExchangePurseDeposits = {
@@ -770,6 +782,7 @@ export async function preparePeerPullPayment(
     amountRaw: req.amount,
   };
 }
+
 /**
  * Initiate a peer pull payment.
  */

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