summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_db.c')
-rw-r--r--src/exchange/taler-exchange-httpd_db.c159
1 files changed, 81 insertions, 78 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 3c6936497..6fec3fee4 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
@@ -19,45 +19,35 @@
* @author Christian Grothoff
*/
#include "platform.h"
+#include <gnunet/gnunet_db_lib.h>
#include <pthread.h>
#include <jansson.h>
#include <gnunet/gnunet_json_lib.h>
+#include "taler_error_codes.h"
+#include "taler_exchangedb_plugin.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
+#include "taler_exchangedb_lib.h"
+#include "taler-exchange-httpd_db.h"
#include "taler-exchange-httpd_responses.h"
-/**
- * How often should we retry a transaction before giving up
- * (for transactions resulting in serialization/dead locks only).
- *
- * The current value is likely too high for production. We might want to
- * benchmark good values once we have a good database setup. The code is
- * expected to work correctly with any positive value, albeit inefficiently if
- * we too aggressively force clients to retry the HTTP request merely because
- * we have database serialization issues.
- */
-#define MAX_TRANSACTION_COMMIT_RETRIES 100
-
-
-/**
- * Ensure coin is known in the database, and handle conflicts and errors.
- *
- * @param coin the coin to make known
- * @param connection MHD request context
- * @param[out] mhd_ret set to MHD status on error
- * @return transaction status, negative on error (@a mhd_ret will be set in this case)
- */
enum GNUNET_DB_QueryStatus
TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
struct MHD_Connection *connection,
+ uint64_t *known_coin_id,
MHD_RESULT *mhd_ret)
{
enum TALER_EXCHANGEDB_CoinKnownStatus cks;
+ struct TALER_DenominationHashP h_denom_pub;
+ struct TALER_AgeCommitmentHash h_age_commitment = {{{0}}};
/* make sure coin is 'known' in database */
cks = TEH_plugin->ensure_coin_known (TEH_plugin->cls,
- coin);
+ coin,
+ known_coin_id,
+ &h_denom_pub,
+ &h_age_commitment);
switch (cks)
{
case TALER_EXCHANGEDB_CKS_ADDED:
@@ -73,60 +63,62 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
TALER_EC_GENERIC_DB_STORE_FAILED,
NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
- case TALER_EXCHANGEDB_CKS_CONFLICT:
- break;
- }
+ case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT:
+ /* The exchange has a seen this coin before, but with a different denomination.
+ * Get the corresponding signature and sent it to the client as proof */
+ {
+ struct
+ {
+ struct TALER_DenominationPublicKey pub;
+ struct TALER_DenominationSignature sig;
+ } prev_denom = {0};
- {
- struct TALER_EXCHANGEDB_TransactionList *tl;
- enum GNUNET_DB_QueryStatus qs;
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ TEH_plugin->get_signature_for_known_coin (TEH_plugin->cls,
+ &coin->coin_pub,
+ &prev_denom.pub,
+ &prev_denom.sig))
+ {
+ /* There _should_ have been a result, because
+ * we ended here due to a conflict! */
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
- qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- &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;
+ *mhd_ret = TEH_RESPONSE_reply_coin_denomination_conflict (
+ connection,
+ TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
+ &coin->coin_pub,
+ &prev_denom.pub,
+ &prev_denom.sig);
+
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- *mhd_ret
- = TEH_RESPONSE_reply_coin_insufficient_funds (
- connection,
- TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
- &coin->coin_pub,
- tl);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
+ case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NULL:
+ case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_EXPECTED_NON_NULL:
+ case TALER_EXCHANGEDB_CKS_AGE_CONFLICT_VALUE_DIFFERS:
+ *mhd_ret = TEH_RESPONSE_reply_coin_age_commitment_conflict (
+ connection,
+ TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH,
+ cks,
+ &h_denom_pub,
+ &coin->coin_pub,
+ &h_age_commitment);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+ GNUNET_assert (0);
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
-/**
- * Run a database transaction for @a connection.
- * Starts a transaction and calls @a cb. Upon success,
- * attempts to commit the transaction. Upon soft failures,
- * retries @a cb a few times. Upon hard or persistent soft
- * errors, generates an error message for @a connection.
- *
- * @param connection MHD connection to run @a cb for, can be NULL
- * @param name name of the transaction (for debugging)
- * @param[out] mhd_ret set to MHD response code, if transaction failed;
- * NULL if we are not running with a @a connection and thus
- * must not queue MHD replies
- * @param cb callback implementing transaction logic
- * @param cb_cls closure for @a cb, must be read-only!
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
- */
enum GNUNET_GenericReturnValue
TEH_DB_run_transaction (struct MHD_Connection *connection,
const char *name,
+ enum TEH_MetricTypeRequest mt,
MHD_RESULT *mhd_ret,
TEH_DB_TransactionCallback cb,
void *cb_cls)
@@ -144,6 +136,8 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
NULL);
return GNUNET_SYSERR;
}
+ GNUNET_assert (mt < TEH_MT_REQUEST_COUNT);
+ TEH_METRICS_num_requests[mt]++;
for (unsigned int retries = 0;
retries < MAX_TRANSACTION_COMMIT_RETRIES;
retries++)
@@ -166,26 +160,35 @@ TEH_DB_run_transaction (struct MHD_Connection *connection,
connection,
mhd_ret);
if (0 > qs)
+ {
TEH_plugin->rollback (TEH_plugin->cls);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- return GNUNET_SYSERR;
- if (0 <= qs)
- qs = TEH_plugin->commit (TEH_plugin->cls);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ return GNUNET_SYSERR;
+ }
+ else
{
- if (NULL != mhd_ret)
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- NULL);
- return GNUNET_SYSERR;
+ qs = TEH_plugin->commit (TEH_plugin->cls);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TEH_plugin->rollback (TEH_plugin->cls);
+ if (NULL != mhd_ret)
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_COMMIT_FAILED,
+ NULL);
+ return GNUNET_SYSERR;
+ }
+ if (0 > qs)
+ TEH_plugin->rollback (TEH_plugin->cls);
}
/* make sure callback did not violate invariants! */
GNUNET_assert ( (NULL == mhd_ret) ||
- (-1 == *mhd_ret) );
+ (-1 == (int) *mhd_ret) );
if (0 <= qs)
return GNUNET_OK;
+ TEH_METRICS_num_conflict[mt]++;
}
+ TEH_plugin->rollback (TEH_plugin->cls);
TALER_LOG_ERROR ("Transaction `%s' commit failed %u times\n",
name,
MAX_TRANSACTION_COMMIT_RETRIES);