gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: optimize /deposit logic to minim


From: gnunet
Subject: [taler-exchange] branch master updated: optimize /deposit logic to minimize serialization failures (presumably)
Date: Wed, 08 Dec 2021 20:33:18 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new e0700ad9 optimize /deposit logic to minimize serialization failures 
(presumably)
e0700ad9 is described below

commit e0700ad9164867c9209beec09b8001f1741eea15
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Wed Dec 8 20:33:14 2021 +0100

    optimize /deposit logic to minimize serialization failures (presumably)
---
 contrib/gana                                       |   2 +-
 src/exchange/taler-exchange-httpd_db.c             | 167 +++++++++++++++++-
 src/exchange/taler-exchange-httpd_db.h             |  26 +++
 src/exchange/taler-exchange-httpd_deposit.c        | 154 +++++++----------
 src/exchange/taler-exchange-httpd_melt.c           | 186 +--------------------
 .../taler-exchange-httpd_refreshes_reveal.c        |  65 +++----
 src/exchangedb/plugin_exchangedb_postgres.c        |   3 +-
 src/lib/exchange_api_deposit.c                     |   2 +-
 src/lib/exchange_api_melt.c                        |   8 +-
 9 files changed, 295 insertions(+), 318 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index b7320181..b1231403 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit b7320181c5e0d95c6f2e2a9e5c53dce0bc1a35a8
+Subproject commit b123140349c3e3b300878d2e35cea1553c9a381d
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index c091f994..383a7b29 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
+  Copyright (C) 2014-2017, 2021 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -28,6 +28,55 @@
 #include "taler-exchange-httpd_responses.h"
 
 
+/**
+ * Send a response for a failed request.  The transaction history of the given
+ * coin demonstrates that the @a residual value of the coin is below the @a
+ * requested contribution of the coin for the operation.  Thus, the exchange
+ * refuses the operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute, including 
fee
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+static MHD_RESULT
+reply_insufficient_funds (
+  struct MHD_Connection *connection,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *coin_value,
+  struct TALER_EXCHANGEDB_TransactionList *tl,
+  const struct TALER_Amount *requested,
+  const struct TALER_Amount *residual)
+{
+  json_t *history;
+
+  history = TEH_RESPONSE_compile_transaction_history (coin_pub,
+                                                      tl);
+  if (NULL == history)
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_EXCHANGE_GENERIC_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
+                                       NULL);
+  return TALER_MHD_REPLY_JSON_PACK (
+    connection,
+    MHD_HTTP_CONFLICT,
+    TALER_JSON_pack_ec (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS),
+    GNUNET_JSON_pack_data_auto ("coin_pub",
+                                coin_pub),
+    TALER_JSON_pack_amount ("original_value",
+                            coin_value),
+    TALER_JSON_pack_amount ("residual_value",
+                            residual),
+    TALER_JSON_pack_amount ("requested_value",
+                            requested),
+    GNUNET_JSON_pack_array_steal ("history",
+                                  history));
+}
+
+
 /**
  * How often should we retry a transaction before giving up
  * (for transactions resulting in serialization/dead locks only).
@@ -114,6 +163,122 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo 
*coin,
 }
 
 
+enum GNUNET_DB_QueryStatus
+TEH_check_coin_balance (struct MHD_Connection *connection,
+                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                        const struct TALER_Amount *coin_value,
+                        const struct TALER_Amount *op_cost,
+                        bool check_recoup,
+                        bool zombie_required,
+                        MHD_RESULT *mhd_ret)
+{
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  struct TALER_Amount spent;
+  enum GNUNET_DB_QueryStatus qs;
+
+  /* Start with zero cost, as we already added this melt transaction
+     to the DB, so we will see it again during the queries below. */
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (TEH_currency,
+                                        &spent));
+
+  /* get historic transaction costs of this coin, including recoups as
+     we might be a zombie coin */
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          coin_pub,
+                                          check_recoup,
+                                          &tl);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                             "coin transaction history");
+    return qs;
+  }
+  if (zombie_required)
+  {
+    /* The denomination key is only usable for a melt if this is a true
+       zombie coin, i.e. it was refreshed and the resulting fresh coin was
+       then recouped. Check that this is truly the case. */
+    for (struct TALER_EXCHANGEDB_TransactionList *tp = tl;
+         NULL != tp;
+         tp = tp->next)
+    {
+      if (TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP == tp->type)
+      {
+        zombie_required = false; /* clear flag: was satisfied! */
+        break;
+      }
+    }
+    if (zombie_required)
+    {
+      /* zombie status not satisfied */
+      GNUNET_break_op (0);
+      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                              tl);
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_BAD_REQUEST,
+                                             
TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
+                                             NULL);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  }
+  if (GNUNET_OK !=
+      TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
+                                                          &spent,
+                                                          &spent))
+  {
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           
TALER_EC_EXCHANGE_GENERIC_COIN_HISTORY_COMPUTATION_FAILED,
+                                           NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* Refuse to refresh when the coin's value is insufficient
+     for the cost of all transactions. */
+  if (0 > TALER_amount_cmp (coin_value,
+                            &spent))
+  {
+    struct TALER_Amount coin_residual;
+    struct TALER_Amount spent_already;
+
+    /* First subtract the melt cost from 'spent' to
+       compute the total amount already spent of the coin */
+    GNUNET_assert (0 <=
+                   TALER_amount_subtract (&spent_already,
+                                          &spent,
+                                          op_cost));
+    /* The residual coin value is the original coin value minus
+       what we have spent (before the melt) */
+    GNUNET_assert (0 <=
+                   TALER_amount_subtract (&coin_residual,
+                                          coin_value,
+                                          &spent_already));
+    *mhd_ret = reply_insufficient_funds (
+      connection,
+      coin_pub,
+      coin_value,
+      tl,
+      op_cost,
+      &coin_residual);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* we're good, coin has sufficient funds to be melted */
+  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                          tl);
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
 enum GNUNET_GenericReturnValue
 TEH_DB_run_transaction (struct MHD_Connection *connection,
                         const char *name,
diff --git a/src/exchange/taler-exchange-httpd_db.h 
b/src/exchange/taler-exchange-httpd_db.h
index 83302235..60885dbd 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -41,6 +41,32 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
                      MHD_RESULT *mhd_ret);
 
 
+/**
+ * Check that a coin has an adequate balance so that we can
+ * commit the current transaction. If the balance is
+ * insufficient for all transactions associated with the
+ * coin, return a hard error.
+ *
+ * @param connection HTTP connection to report hard errors on
+ * @param coin_pub coin to analyze
+ * @param coin_value total value of the original coin (by denomination)
+ * @param op_cost cost of the current operation (for error reporting)
+ * @param check_recoup should we include recoup transactions in the check
+ * @param zombie_required additional requirement that the coin must
+ *          be a zombie coin, or also hard failure
+ * @param[out] mhd_ret set to response status code, on hard error only
+ * @return transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_check_coin_balance (struct MHD_Connection *connection,
+                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                        const struct TALER_Amount *coin_value,
+                        const struct TALER_Amount *op_cost,
+                        bool check_recoup,
+                        bool zombie_required,
+                        MHD_RESULT *mhd_ret);
+
+
 /**
  * Function implementing a database transaction.  Runs the transaction
  * logic; IF it returns a non-error code, the transaction logic MUST
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index d549a1fa..43acad44 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -162,113 +162,85 @@ deposit_transaction (void *cls,
   enum GNUNET_DB_QueryStatus qs;
   struct TALER_Amount deposit_fee;
 
-  /* Check for idempotency: did we get this request before? */
-  qs = TEH_plugin->have_deposit (TEH_plugin->cls,
-                                 deposit,
-                                 &deposit_fee,
-                                 &dc->exchange_timestamp);
+  /* begin optimistically: assume this is a new deposit */
+  qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
+                                   dc->exchange_timestamp,
+                                   deposit);
   if (qs < 0)
   {
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           NULL);
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    /* Check for idempotency: did we get this request before? */
+    qs = TEH_plugin->have_deposit (TEH_plugin->cls,
+                                   deposit,
+                                   &deposit_fee,
+                                   &dc->exchange_timestamp);
+    if (qs < 0)
     {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        return qs;
       *mhd_ret = TALER_MHD_reply_with_error (connection,
                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
                                              "have_deposit");
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    return qs;
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-  {
-    struct TALER_Amount amount_without_fee;
-
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "/deposit replay, accepting again!\n");
-    GNUNET_assert (0 <=
-                   TALER_amount_subtract (&amount_without_fee,
-                                          &deposit->amount_with_fee,
-                                          &deposit_fee));
-    *mhd_ret = reply_deposit_success (connection,
-                                      &deposit->coin.coin_pub,
-                                      &dc->h_wire,
-                                      NULL /* h_extensions! */,
-                                      &deposit->h_contract_terms,
-                                      dc->exchange_timestamp,
-                                      deposit->refund_deadline,
-                                      deposit->wire_deadline,
-                                      &deposit->merchant_pub,
-                                      &amount_without_fee);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-
-  /* Start with fee for THIS transaction */
-  spent = deposit->amount_with_fee;
-  /* add cost of all previous transactions; skip RECOUP as revoked
-     denominations are not eligible for deposit, and if we are the old coin
-     pub of a revoked coin (aka a zombie), then ONLY refresh is allowed. */
-  {
-    struct TALER_EXCHANGEDB_TransactionList *tl;
-
-    qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                            &deposit->coin.coin_pub,
-                                            GNUNET_NO,
-                                            &tl);
-    if (0 > qs)
-    {
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (
-          connection,
-          MHD_HTTP_INTERNAL_SERVER_ERROR,
-          TALER_EC_GENERIC_DB_FETCH_FAILED,
-          NULL);
-      return qs;
-    }
-    if (GNUNET_OK !=
-        TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
-                                                            &spent, /* 
starting offset */
-                                                            &spent /* result 
*/))
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
-      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                              tl);
-      *mhd_ret = TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_INTERNAL_SERVER_ERROR,
-        TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
-        NULL);
+      /* Conflict on insert, but record does not exist?
+         That makes no sense. */
+      GNUNET_break (0);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    /* Check that cost of all transactions (including the current one) is
-       smaller (or equal) than the value of the coin. */
-    if (0 < TALER_amount_cmp (&spent,
-                              &dc->value))
+
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Deposited coin has insufficient funds left!\n");
-      *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
-                                                             
TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS,
-                                                             &deposit->coin.
-                                                             coin_pub,
-                                                             tl);
-      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                              tl);
+      struct TALER_Amount amount_without_fee;
+
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "/deposit replay, accepting again!\n");
+      GNUNET_assert (0 <=
+                     TALER_amount_subtract (&amount_without_fee,
+                                            &deposit->amount_with_fee,
+                                            &deposit_fee));
+      *mhd_ret = reply_deposit_success (connection,
+                                        &deposit->coin.coin_pub,
+                                        &dc->h_wire,
+                                        NULL /* h_extensions! */,
+                                        &deposit->h_contract_terms,
+                                        dc->exchange_timestamp,
+                                        deposit->refund_deadline,
+                                        deposit->wire_deadline,
+                                        &deposit->merchant_pub,
+                                        &amount_without_fee);
+      /* Note: we return "hard error" to ensure the wrapper
+         does not retry the transaction, and to also not generate
+         a "fresh" response (as we would on "success") */
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
   }
-  qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
-                                   dc->exchange_timestamp,
-                                   deposit);
-  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-  {
-    TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           TALER_EC_GENERIC_DB_STORE_FAILED,
-                                           NULL);
-  }
-  return qs;
+
+  /* Start with zero cost, as we already added this melt transaction
+     to the DB, so we will see it again during the queries below. */
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (TEH_currency,
+                                        &spent));
+
+  return TEH_check_coin_balance (connection,
+                                 &deposit->coin.coin_pub,
+                                 &dc->value,
+                                 &deposit->amount_with_fee,
+                                 false, /* no need for recoup */
+                                 false, /* no need for zombie */
+                                 mhd_ret);
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
index c00eb8af..ab7bed29 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -33,56 +33,6 @@
 #include "taler_exchangedb_lib.h"
 
 
-/**
- * Send a response for a failed "melt" request.  The
- * transaction history of the given coin demonstrates that the
- * @a residual value of the coin is below the @a requested
- * contribution of the coin for the melt.  Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute, including 
fee
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-static MHD_RESULT
-reply_melt_insufficient_funds (
-  struct MHD_Connection *connection,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
-  const struct TALER_Amount *coin_value,
-  struct TALER_EXCHANGEDB_TransactionList *tl,
-  const struct TALER_Amount *requested,
-  const struct TALER_Amount *residual)
-{
-  json_t *history;
-
-  history = TEH_RESPONSE_compile_transaction_history (coin_pub,
-                                                      tl);
-  if (NULL == history)
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
-                                       NULL);
-  return TALER_MHD_REPLY_JSON_PACK (
-    connection,
-    MHD_HTTP_CONFLICT,
-    TALER_JSON_pack_ec (TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS),
-    GNUNET_JSON_pack_data_auto ("coin_pub",
-                                coin_pub),
-    TALER_JSON_pack_amount ("original_value",
-                            coin_value),
-    TALER_JSON_pack_amount ("residual_value",
-                            residual),
-    TALER_JSON_pack_amount ("requested_value",
-                            requested),
-    GNUNET_JSON_pack_array_steal ("history",
-                                  history));
-}
-
-
 /**
  * Send a response to a "melt" request.
  *
@@ -165,127 +115,6 @@ struct MeltContext
 };
 
 
-/**
- * Check that the coin has sufficient funds left for the selected
- * melt operation.
- *
- * @param connection the connection to send errors to
- * @param[in,out] rmc melt context
- * @param[out] mhd_ret status code to return to MHD on hard error
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-refresh_check_melt (struct MHD_Connection *connection,
-                    struct MeltContext *rmc,
-                    MHD_RESULT *mhd_ret)
-{
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_Amount spent;
-  enum GNUNET_DB_QueryStatus qs;
-
-  /* Start with zero cost, as we already added this melt transaction
-     to the DB, so we will see it again during the queries below. */
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (TEH_currency,
-                                        &spent));
-
-  /* get historic transaction costs of this coin, including recoups as
-     we might be a zombie coin */
-  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          &rmc->refresh_session.coin.coin_pub,
-                                          GNUNET_YES,
-                                          &tl);
-  if (0 > qs)
-  {
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                             "coin transaction history");
-    return qs;
-  }
-  if (rmc->zombie_required)
-  {
-    /* The denomination key is only usable for a melt if this is a true
-       zombie coin, i.e. it was refreshed and the resulting fresh coin was
-       then recouped. Check that this is truly the case. */
-    for (struct TALER_EXCHANGEDB_TransactionList *tp = tl;
-         NULL != tp;
-         tp = tp->next)
-    {
-      if (TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP == tp->type)
-      {
-        rmc->zombie_required = false; /* clear flag: was satisfied! */
-        break;
-      }
-    }
-    if (rmc->zombie_required)
-    {
-      /* zombie status not satisfied */
-      GNUNET_break_op (0);
-      TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                              tl);
-      *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_BAD_REQUEST,
-                                             
TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
-                                             NULL);
-      return GNUNET_DB_STATUS_HARD_ERROR;
-    }
-  }
-  if (GNUNET_OK !=
-      TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
-                                                          &spent,
-                                                          &spent))
-  {
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           
TALER_EC_EXCHANGE_MELT_COIN_HISTORY_COMPUTATION_FAILED,
-                                           NULL);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-
-  /* Refuse to refresh when the coin's value is insufficient
-     for the cost of all transactions. */
-  if (0 > TALER_amount_cmp (&rmc->coin_value,
-                            &spent))
-  {
-    struct TALER_Amount coin_residual;
-    struct TALER_Amount spent_already;
-
-    /* First subtract the melt cost from 'spent' to
-       compute the total amount already spent of the coin */
-    GNUNET_assert (0 <=
-                   TALER_amount_subtract (&spent_already,
-                                          &spent,
-                                          
&rmc->refresh_session.amount_with_fee));
-    /* The residual coin value is the original coin value minus
-       what we have spent (before the melt) */
-    GNUNET_assert (0 <=
-                   TALER_amount_subtract (&coin_residual,
-                                          &rmc->coin_value,
-                                          &spent_already));
-    *mhd_ret = reply_melt_insufficient_funds (
-      connection,
-      &rmc->refresh_session.coin.coin_pub,
-      &rmc->coin_value,
-      tl,
-      &rmc->refresh_session.amount_with_fee,
-      &coin_residual);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-
-  /* we're good, coin has sufficient funds to be melted */
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
 /**
  * Execute a "melt".  We have been given a list of valid
  * coins and a request to melt them into the given @a
@@ -366,14 +195,13 @@ melt_transaction (void *cls,
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
   }
-
-  /* check coin has enough funds remaining on it to cover melt cost */
-  qs = refresh_check_melt (connection,
-                           rmc,
-                           mhd_ret);
-  if (0 > qs)
-    return qs; /* if we failed, tell caller */
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  return TEH_check_coin_balance (connection,
+                                 &rmc->refresh_session.coin.coin_pub,
+                                 &rmc->coin_value,
+                                 &rmc->refresh_session.amount_with_fee,
+                                 true,
+                                 rmc->zombie_required,
+                                 mhd_ret);
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 08587a51..6c1766fe 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -105,6 +105,11 @@ struct RevealContext
    */
   struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
 
+  /**
+   * Melt data for our session we got from the database for @e rc.
+   */
+  struct TALER_EXCHANGEDB_Melt melt;
+
   /**
    * Denominations being requested.
    */
@@ -266,35 +271,6 @@ refreshes_reveal_transaction (void *cls,
                               MHD_RESULT *mhd_ret)
 {
   struct RevealContext *rctx = cls;
-  struct TALER_EXCHANGEDB_Melt melt;
-  enum GNUNET_DB_QueryStatus qs;
-
-  /* Obtain basic information about the refresh operation and what
-     gamma we committed to. */
-  // FIXME: why do we do 'get_melt' twice?
-  qs = TEH_plugin->get_melt (TEH_plugin->cls,
-                             &rctx->rc,
-                             &melt);
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-  {
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_NOT_FOUND,
-                                           
TALER_EC_EXCHANGE_REFRESHES_REVEAL_SESSION_UNKNOWN,
-                                           NULL);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-    return qs;
-  if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
-       (melt.session.noreveal_index >= TALER_CNC_KAPPA) )
-  {
-    GNUNET_break (0);
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                           "melt");
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
 
   /* Verify commitment */
   {
@@ -310,7 +286,7 @@ refreshes_reveal_transaction (void *cls,
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == melt.session.noreveal_index)
+      if (i == rctx->melt.session.noreveal_index)
       {
         /* Take these coin envelopes from the client */
         rce->transfer_pub = rctx->gamma_tp;
@@ -327,7 +303,7 @@ refreshes_reveal_transaction (void *cls,
         GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
                                             &rce->transfer_pub.ecdhe_pub);
         TALER_link_reveal_transfer_secret (tpriv,
-                                           &melt.session.coin.coin_pub,
+                                           &rctx->melt.session.coin.coin_pub,
                                            &ts);
         rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
                                            struct TALER_RefreshCoinData);
@@ -356,15 +332,15 @@ refreshes_reveal_transaction (void *cls,
                                   TALER_CNC_KAPPA,
                                   rctx->num_fresh_coins,
                                   rcs,
-                                  &melt.session.coin.coin_pub,
-                                  &melt.session.amount_with_fee);
+                                  &rctx->melt.session.coin.coin_pub,
+                                  &rctx->melt.session.amount_with_fee);
 
     /* Free resources allocated above */
     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == melt.session.noreveal_index)
+      if (i == rctx->melt.session.noreveal_index)
         continue; /* This offset is special: not allocated! */
       for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
       {
@@ -395,7 +371,7 @@ refreshes_reveal_transaction (void *cls,
   {
     struct TALER_Amount refresh_cost;
 
-    refresh_cost = melt.melt_fee;
+    refresh_cost = rctx->melt.melt_fee;
     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     {
       struct TALER_Amount total;
@@ -418,7 +394,7 @@ refreshes_reveal_transaction (void *cls,
       }
     }
     if (0 < TALER_amount_cmp (&refresh_cost,
-                              &melt.session.amount_with_fee))
+                              &rctx->melt.session.amount_with_fee))
     {
       GNUNET_break_op (0);
       *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -505,7 +481,6 @@ resolve_refreshes_reveal_denominations (struct 
MHD_Connection *connection,
   struct TALER_DenominationHash dk_h[num_fresh_coins];
   struct TALER_RefreshCoinData rcds[num_fresh_coins];
   struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
-  struct TALER_EXCHANGEDB_Melt melt;
   enum GNUNET_GenericReturnValue res;
   MHD_RESULT ret;
   struct TEH_KeyStateHandle *ksh;
@@ -612,11 +587,10 @@ resolve_refreshes_reveal_denominations (struct 
MHD_Connection *connection,
   {
     enum GNUNET_DB_QueryStatus qs;
 
-    // FIXME: why do we do 'get_melt' twice?
     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
         (qs = TEH_plugin->get_melt (TEH_plugin->cls,
                                     &rctx->rc,
-                                    &melt)))
+                                    &rctx->melt)))
     {
       switch (qs)
       {
@@ -643,6 +617,17 @@ resolve_refreshes_reveal_denominations (struct 
MHD_Connection *connection,
       }
       goto cleanup;
     }
+    /* Obtain basic information about the refresh operation and what
+       gamma we committed to. */
+    if (rctx->melt.session.noreveal_index >= TALER_CNC_KAPPA)
+    {
+      GNUNET_break (0);
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                        "melt");
+      goto cleanup;
+    }
   }
   /* Parse link signatures array */
   for (unsigned int i = 0; i<num_fresh_coins; i++)
@@ -666,7 +651,7 @@ resolve_refreshes_reveal_denominations (struct 
MHD_Connection *connection,
           &rctx->gamma_tp,
           rcds[i].coin_ev,
           rcds[i].coin_ev_size,
-          &melt.session.coin.coin_pub,
+          &rctx->melt.session.coin.coin_pub,
           &link_sigs[i]))
     {
       GNUNET_break_op (0);
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 33383df5..73a03e75 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1034,7 +1034,8 @@ prepare_statements (struct PostgresClosure *pg)
       ") SELECT known_coin_id, $2, $3, $4, $5, $6, "
       " $7, $8, $9, $10, $11, $12, $13"
       "    FROM known_coins"
-      "   WHERE coin_pub=$1;",
+      "   WHERE coin_pub=$1"
+      " ON CONFLICT DO NOTHING;",
       13),
     /* Fetch an existing deposit request, used to ensure idempotency
        during /deposit processing. Used in #postgres_have_deposit(). */
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 8b93f56e..3fd3353b 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -263,7 +263,7 @@ verify_deposit_signature_conflict (
   ec = TALER_JSON_get_error_code (json);
   switch (ec)
   {
-  case TALER_EC_EXCHANGE_DEPOSIT_INSUFFICIENT_FUNDS:
+  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
     if (0 >
         TALER_amount_add (&total,
                           &total,
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index f3032e8b..6ef9f4cb 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -97,7 +97,7 @@ struct TALER_EXCHANGE_MeltHandle
  * @param[out] noreveal_index set to the noreveal index selected by the 
exchange
  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
  */
-static int
+static enum GNUNET_GenericReturnValue
 verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
                           const json_t *json,
                           struct TALER_ExchangePublicKeyP *exchange_pub,
@@ -208,7 +208,7 @@ verify_melt_signature_denom_conflict (struct 
TALER_EXCHANGE_MeltHandle *mh,
  * @param json json reply with the signature(s) and transaction history
  * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
  */
-static int
+static enum GNUNET_GenericReturnValue
 verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
                                       const json_t *json)
 {
@@ -282,7 +282,7 @@ verify_melt_signature_spend_conflict (struct 
TALER_EXCHANGE_MeltHandle *mh,
   ec = TALER_JSON_get_error_code (json);
   switch (ec)
   {
-  case TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS:
+  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
     /* check if melt operation was really too expensive given history */
     if (0 >
         TALER_amount_add (&total,
@@ -379,7 +379,7 @@ handle_melt_finished (void *cls,
     hr.ec = TALER_JSON_get_error_code (j);
     switch (hr.ec)
     {
-    case TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS:
+    case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
       /* Double spending; check signatures on transaction history */
       if (GNUNET_OK !=
           verify_melt_signature_spend_conflict (mh,

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