summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-12-09 23:13:39 +0100
committerChristian Grothoff <christian@grothoff.org>2021-12-09 23:13:39 +0100
commitfba91c63d57d73732249b972127575ca1fd4d5ff (patch)
tree9ceedd346da020458124a235f928c9408a9df31b /src/exchange
parent889625a90f97a23048b3c9dad418f86acb81314b (diff)
downloadexchange-fba91c63d57d73732249b972127575ca1fd4d5ff.tar.gz
exchange-fba91c63d57d73732249b972127575ca1fd4d5ff.tar.bz2
exchange-fba91c63d57d73732249b972127575ca1fd4d5ff.zip
introduce stored procedure for coin balance check
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/taler-exchange-httpd_db.c110
-rw-r--r--src/exchange/taler-exchange-httpd_db.h3
2 files changed, 104 insertions, 9 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 7ced8b88e..388679c38 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -164,14 +164,29 @@ 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)
+/**
+ * Called when we actually know that the balance (was) insufficient.
+ * Re-does the check (slowly) to compute the full error message for
+ * the client.
+ *
+ * @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
+ */
+static enum GNUNET_DB_QueryStatus
+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;
@@ -273,13 +288,90 @@ TEH_check_coin_balance (struct MHD_Connection *connection,
return GNUNET_DB_STATUS_HARD_ERROR;
}
- /* we're good, coin has sufficient funds to be melted */
+ /* This should not happen: The coin has sufficient funds
+ after all!?!? */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
+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)
+{
+ bool balance_ok = false;
+ bool zombie_ok = false;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->do_check_coin_balance (TEH_plugin->cls,
+ coin_pub,
+ coin_value,
+ check_recoup,
+ zombie_required,
+ &balance_ok,
+ &zombie_ok);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "check_coin_balance");
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "check_coin_balance");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* handled below */
+ break;
+ }
+ if (! zombie_ok)
+ {
+ GNUNET_break_op (0);
+ *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 (balance_ok)
+ return qs;
+ /* balance is not OK, do expensive call to compute full error message */
+ qs = check_coin_balance (connection,
+ coin_pub,
+ coin_value,
+ op_cost,
+ check_recoup,
+ zombie_required,
+ mhd_ret);
+ if (qs < 0)
+ return qs; /* we expected to fail (same check as before!) */
+ GNUNET_break (0); /* stored procedure and individual statements
+ disagree, should be impossible! */
+ *mhd_ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ "stored procedure disagrees with full coin transaction history fetch");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+}
+
+
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 60885dbd1..5ee3b41d5 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -47,6 +47,9 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
* insufficient for all transactions associated with the
* coin, return a hard error.
*
+ * We first do a "fast" check using a stored procedure, and
+ * only obtain the "full" data on failure (for performance).
+ *
* @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)