aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-06-18 21:47:05 +0200
committerChristian Grothoff <christian@grothoff.org>2017-06-19 00:17:15 +0200
commit2ec1b055a0f574e3837f2fbbb38098db4eb65f6f (patch)
tree8070390b91f43d07f68b7f2d785a3dd68c593658
parentd2c7ef54a7eb906b40032969b5bc45c180003f4b (diff)
refactoring /deposit towards new transaction style (#5010)
-rw-r--r--src/exchange/taler-exchange-httpd_db.c249
-rw-r--r--src/exchange/taler-exchange-httpd_db.h61
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c215
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c54
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h29
-rw-r--r--src/exchangedb/perf_taler_exchangedb_interpreter.c4
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c129
-rw-r--r--src/exchangedb/test_exchangedb.c8
-rw-r--r--src/include/taler_exchangedb_plugin.h14
9 files changed, 422 insertions, 341 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index abbb6c3fe..2cdf61d8d 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -113,19 +113,92 @@ transaction_start_label: /* we will use goto for retries */ \
/**
+ * 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
+ * @param[out] set to MHD response code, if transaction failed
+ * @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
+ */
+int
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+ int *mhd_ret,
+ TEH_DB_TransactionCallback cb,
+ void *cb_cls)
+{
+ struct TALER_EXCHANGEDB_Session *session;
+
+ *mhd_ret = -1; /* invalid value */
+ if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_DB_SETUP_FAILED);
+ return GNUNET_SYSERR;
+ }
+ for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; retries++)
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ if (GNUNET_OK !=
+ TEH_plugin->start (TEH_plugin->cls,
+ session))
+ {
+ GNUNET_break (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_DB_START_FAILED);
+ return GNUNET_SYSERR;
+ }
+ qs = cb (cb_cls,
+ connection,
+ session,
+ mhd_ret);
+ if (0 > qs)
+ TEH_plugin->rollback (TEH_plugin->cls,
+ session);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ return GNUNET_SYSERR;
+ if (0 <= qs)
+ qs = TEH_plugin->commit (TEH_plugin->cls,
+ session);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+ TALER_EC_DB_COMMIT_FAILED_HARD);
+ return GNUNET_SYSERR;
+ }
+ /* make sure callback did not violate invariants! */
+ GNUNET_assert (-1 == *mhd_ret);
+ if (0 <= qs)
+ return GNUNET_OK;
+ }
+ TALER_LOG_WARNING ("Transaction commit failed %u times\n",
+ MAX_TRANSACTION_COMMIT_RETRIES);
+ *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+ TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Calculate the total value of all transactions performed.
* Stores @a off plus the cost of all transactions in @a tl
* in @a ret.
*
* @param tl transaction list to process
* @param off offset to use as the starting value
- * @param ret where the resulting total is to be stored
+ * @param[out] ret where the resulting total is to be stored
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
-static int
-calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
- const struct TALER_Amount *off,
- struct TALER_Amount *ret)
+int
+TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
+ const struct TALER_Amount *off,
+ struct TALER_Amount *ret)
{
struct TALER_Amount spent = *off;
struct TALER_EXCHANGEDB_TransactionList *pos;
@@ -207,146 +280,6 @@ calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
/**
- * Execute a deposit. The validity of the coin and signature
- * have already been checked. The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
- */
-int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Deposit *deposit)
-{
- struct TALER_EXCHANGEDB_Session *session;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_Amount spent;
- struct TALER_Amount value;
- struct TALER_Amount amount_without_fee;
- struct TEH_KS_StateHandle *mks;
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- int ret;
- enum GNUNET_DB_QueryStatus qs;
- unsigned int retries = 0;
-
- if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
- {
- GNUNET_break (0);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DB_SETUP_FAILED);
- }
- again:
- if (GNUNET_YES ==
- TEH_plugin->have_deposit (TEH_plugin->cls,
- session,
- deposit))
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&amount_without_fee,
- &deposit->amount_with_fee,
- &deposit->deposit_fee));
- return TEH_RESPONSE_reply_deposit_success (connection,
- &deposit->coin.coin_pub,
- &deposit->h_wire,
- &deposit->h_contract_terms,
- deposit->timestamp,
- deposit->refund_deadline,
- &deposit->merchant_pub,
- &amount_without_fee);
- }
-
- /* FIXME: move the 'mks'-logic outside of _db.c? */
- mks = TEH_KS_acquire ();
- dki = TEH_KS_denomination_key_lookup (mks,
- &deposit->coin.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == dki)
- {
- TEH_KS_release (mks);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
- }
- TALER_amount_ntoh (&value,
- &dki->issue.properties.value);
- TEH_KS_release (mks);
-
- START_TRANSACTION (session, connection);
-
- /* fee for THIS transaction */
- spent = deposit->amount_with_fee;
- /* add cost of all previous transactions */
- tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
- session,
- &deposit->coin.coin_pub);
- if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
- }
- /* Check that cost of all transactions is smaller than
- the value of the coin. */
- if (0 < TALER_amount_cmp (&spent,
- &value))
- {
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
- TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
- tl);
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- return ret;
- }
- TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
- tl);
- qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
- session,
- deposit);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DEPOSIT_STORE_DB_ERROR);
- }
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- retries++;
- TEH_plugin->rollback (TEH_plugin->cls,
- session);
- if (retries > 5)
- return TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_DEPOSIT_STORE_DB_ERROR);
- goto again;
- }
-
- COMMIT_TRANSACTION(session, connection);
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_subtract (&amount_without_fee,
- &deposit->amount_with_fee,
- &deposit->deposit_fee));
- return TEH_RESPONSE_reply_deposit_success (connection,
- &deposit->coin.coin_pub,
- &deposit->h_wire,
- &deposit->h_contract_terms,
- deposit->timestamp,
- deposit->refund_deadline,
- &deposit->merchant_pub,
- &amount_without_fee);
-}
-
-
-/**
* Execute a "/refund". Returns a confirmation that the refund
* was successful, or a failure if we are not aware of a matching
* /deposit or if it is too late to do the refund.
@@ -367,6 +300,7 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
struct TEH_KS_StateHandle *mks;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_Amount expect_fee;
+ enum GNUNET_DB_QueryStatus qs;
int ret;
int deposit_found;
int refund_found;
@@ -595,10 +529,10 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
tl);
/* Finally, store new refund data */
- if (GNUNET_OK !=
- TEH_plugin->insert_refund (TEH_plugin->cls,
- session,
- refund))
+ qs = TEH_plugin->insert_refund (TEH_plugin->cls,
+ session,
+ refund);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
TALER_LOG_WARNING ("Failed to store /refund information in database\n");
TEH_plugin->rollback (TEH_plugin->cls,
@@ -606,6 +540,11 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFUND_STORE_DB_ERROR);
}
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ {
+ /* FIXME: #5010: retry! */
+ }
+
COMMIT_TRANSACTION (session, connection);
return TEH_RESPONSE_reply_refund_success (connection,
@@ -1043,9 +982,9 @@ refresh_check_melt (struct MHD_Connection *connection,
session,
&coin_details->coin_info.coin_pub);
if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
{
GNUNET_break (0);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
@@ -2403,9 +2342,9 @@ TEH_DB_execute_payback (struct MHD_Connection *connection,
TALER_amount_get_zero (value->currency,
&spent);
if (GNUNET_OK !=
- calculate_transaction_list_totals (tl,
- &spent,
- &spent))
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
{
GNUNET_break (0);
TEH_plugin->rollback (TEH_plugin->cls,
diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h
index 55faafa06..6d0b7e35a 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -24,20 +24,63 @@
#include <microhttpd.h>
#include "taler_exchangedb_plugin.h"
+/**
+ * Function implementing a database transaction. Runs the transaction
+ * logic; IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls closure
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+typedef enum GNUNET_DB_QueryStatus
+(*TEH_DB_TransactionCallback)(void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret);
+
/**
- * Execute a "/deposit". The validity of the coin and signature
- * have already been checked. The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
+ * 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
+ * @param[out] set to MHD response code, if transaction failed
+ * @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
+ */
+int
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+ int *mhd_ret,
+ TEH_DB_TransactionCallback cb,
+ void *cb_cls);
+
+
+/**
+ * Calculate the total value of all transactions performed.
+ * Stores @a off plus the cost of all transactions in @a tl
+ * in @a ret.
*
- * @param connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
+ * @param tl transaction list to process
+ * @param off offset to use as the starting value
+ * @param[out] ret where the resulting total is to be stored
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/
+// FIXME: maybe move to another module?
int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
- const struct TALER_EXCHANGEDB_Deposit *deposit);
+TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
+ const struct TALER_Amount *off,
+ struct TALER_Amount *ret);
/**
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 85504d8df..d732edbb6 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+ Copyright (C) 2014-2017 Inria and GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -37,6 +37,174 @@
/**
+ * Send confirmation of deposit success to client. This function
+ * will create a signed message affirming the given information
+ * and return it to the client. By this, the exchange affirms that
+ * the coin had sufficient (residual) value for the specified
+ * transaction and that it will execute the requested deposit
+ * operation with the given wiring details.
+ *
+ * @param connection connection to the client
+ * @param coin_pub public key of the coin
+ * @param h_wire hash of wire details
+ * @param h_contract_terms hash of contract details
+ * @param timestamp client's timestamp
+ * @param refund_deadline until when this deposit be refunded
+ * @param merchant merchant public key
+ * @param amount_without_fee fraction of coin value to deposit, without the fee
+ * @return MHD result code
+ */
+static int
+reply_deposit_success (struct MHD_Connection *connection,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct GNUNET_HashCode *h_wire,
+ const struct GNUNET_HashCode *h_contract_terms,
+ struct GNUNET_TIME_Absolute timestamp,
+ struct GNUNET_TIME_Absolute refund_deadline,
+ const struct TALER_MerchantPublicKeyP *merchant,
+ const struct TALER_Amount *amount_without_fee)
+{
+ struct TALER_DepositConfirmationPS dc;
+ struct TALER_ExchangePublicKeyP pub;
+ struct TALER_ExchangeSignatureP sig;
+
+ dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
+ dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
+ dc.h_contract_terms = *h_contract_terms;
+ dc.h_wire = *h_wire;
+ dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+ dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+ TALER_amount_hton (&dc.amount_without_fee,
+ amount_without_fee);
+ dc.coin_pub = *coin_pub;
+ dc.merchant = *merchant;
+ TEH_KS_sign (&dc.purpose,
+ &pub,
+ &sig);
+ return TEH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:s, s:o, s:o}",
+ "status", "DEPOSIT_OK",
+ "sig", GNUNET_JSON_from_data_auto (&sig),
+ "pub", GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #deposit_transaction.
+ */
+struct DepositContext
+{
+ /**
+ * Information about the deposit request.
+ */
+ const struct TALER_EXCHANGEDB_Deposit *deposit;
+
+ /**
+ * Value of the coin.
+ */
+ struct TALER_Amount value;
+
+};
+
+
+/**
+ * Execute database transaction for /deposit. Runs the transaction
+ * logic; IF it returns a non-error code, the transaction logic MUST
+ * NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
+ * it returns the soft error code, the function MAY be called again to
+ * retry and MUST not queue a MHD response.
+ *
+ * @param cls a `struct DepositContext`
+ * @param connection MHD request context
+ * @param session database session and transaction to use
+ * @param[out] mhd_ret set to MHD status on error
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+deposit_transaction (void *cls,
+ struct MHD_Connection *connection,
+ struct TALER_EXCHANGEDB_Session *session,
+ int *mhd_ret)
+{
+ struct DepositContext *dc = cls;
+ const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ struct TALER_Amount spent;
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->have_deposit (TEH_plugin->cls,
+ session,
+ deposit);
+ if (qs < 0)
+ return qs;
+ if (1 == qs)
+ {
+ struct TALER_Amount amount_without_fee;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_subtract (&amount_without_fee,
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee));
+ *mhd_ret = reply_deposit_success (connection,
+ &deposit->coin.coin_pub,
+ &deposit->h_wire,
+ &deposit->h_contract_terms,
+ deposit->timestamp,
+ deposit->refund_deadline,
+ &deposit->merchant_pub,
+ &amount_without_fee);
+ /* Treat as 'hard' DB error as we want to rollback and
+ never try again. */
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ /* Start with fee for THIS transaction */
+ spent = deposit->amount_with_fee;
+ /* add cost of all previous transactions */
+ tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ session,
+ &deposit->coin.coin_pub);
+ if (GNUNET_OK !=
+ TEH_DB_calculate_transaction_list_totals (tl,
+ &spent,
+ &spent))
+ {
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ /* Check that cost of all transactions is smaller than
+ the value of the coin. */
+ if (0 < TALER_amount_cmp (&spent,
+ &dc->value))
+ {
+ *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
+ TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
+ tl);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
+ session,
+ deposit);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_DEPOSIT_STORE_DB_ERROR);
+ }
+ return qs;
+}
+
+
+/**
* We have parsed the JSON information about the deposit, do some
* basic sanity checks (especially that the signature on the coin is
* valid, and that this type of coin exists) and then execute the
@@ -51,7 +219,13 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
struct TALER_DepositRequestPS dr;
+ int mhd_ret;
+ struct TALER_Amount amount_without_fee;
+ struct DepositContext dc;
+ struct TEH_KS_StateHandle *mks;
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+ /* check signature */
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
dr.h_contract_terms = deposit->h_contract_terms;
@@ -76,8 +250,43 @@ verify_and_execute_deposit (struct MHD_Connection *connection,
"coin_sig");
}
- return TEH_DB_execute_deposit (connection,
- deposit);
+ /* check denomination */
+ mks = TEH_KS_acquire ();
+ dki = TEH_KS_denomination_key_lookup (mks,
+ &deposit->coin.denom_pub,
+ TEH_KS_DKU_DEPOSIT);
+ if (NULL == dki)
+ {
+ TEH_KS_release (mks);
+ return TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
+ }
+ TALER_amount_ntoh (&dc.value,
+ &dki->issue.properties.value);
+ TEH_KS_release (mks);
+
+ /* execute transaction */
+ dc.deposit = deposit;
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &deposit_transaction,
+ &dc))
+ return mhd_ret;
+
+ /* generate regular response */
+ GNUNET_assert (GNUNET_SYSERR !=
+ TALER_amount_subtract (&amount_without_fee,
+ &deposit->amount_with_fee,
+ &deposit->deposit_fee));
+ return reply_deposit_success (connection,
+ &deposit->coin.coin_pub,
+ &deposit->h_wire,
+ &deposit->h_contract_terms,
+ deposit->timestamp,
+ deposit->refund_deadline,
+ &deposit->merchant_pub,
+ &amount_without_fee);
}
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 01b5606a5..771b8adfe 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -462,60 +462,6 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection)
/**
- * Send confirmation of deposit success to client. This function
- * will create a signed message affirming the given information
- * and return it to the client. By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
- *
- * @param connection connection to the client
- * @param coin_pub public key of the coin
- * @param h_wire hash of wire details
- * @param h_contract_terms hash of contract details
- * @param timestamp client's timestamp
- * @param refund_deadline until when this deposit be refunded
- * @param merchant merchant public key
- * @param amount_without_fee fraction of coin value to deposit, without the fee
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct GNUNET_HashCode *h_wire,
- const struct GNUNET_HashCode *h_contract_terms,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund_deadline,
- const struct TALER_MerchantPublicKeyP *merchant,
- const struct TALER_Amount *amount_without_fee)
-{
- struct TALER_DepositConfirmationPS dc;
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
-
- dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
- dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
- dc.h_contract_terms = *h_contract_terms;
- dc.h_wire = *h_wire;
- dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
- dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
- TALER_amount_hton (&dc.amount_without_fee,
- amount_without_fee);
- dc.coin_pub = *coin_pub;
- dc.merchant = *merchant;
- TEH_KS_sign (&dc.purpose,
- &pub,
- &sig);
- return TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_OK,
- "{s:s, s:o, s:o}",
- "status", "DEPOSIT_OK",
- "sig", GNUNET_JSON_from_data_auto (&sig),
- "pub", GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
* Compile the transaction history of a coin into a JSON object.
*
* @param tl transaction history to JSON-ify
diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h
index 6a33b65c7..047bb4f37 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -244,35 +244,6 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection *connectionx);
/**
- * Send confirmation of deposit success to client. This function
- * will create a signed message affirming the given information
- * and return it to the client. By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
- *
- * @param connection connection to the client
- * @param coin_pub public key of the coin
- * @param h_wire hash of wire details
- * @param h_contract_terms hash of proposal data
- * @param timestamp client's timestamp
- * @param refund_deadline until when this deposit be refunded
- * @param merchant merchant public key
- * @param amount_without_fee fraction of coin value to deposit (without fee)
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct GNUNET_HashCode *h_wire,
- const struct GNUNET_HashCode *h_contract_terms,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute refund_deadline,
- const struct TALER_MerchantPublicKeyP *merchant,
- const struct TALER_Amount *amount_without_fee);
-
-
-/**
* Send proof that a request is invalid to client because of
* insufficient funds. This function will create a message with all
* of the operations affecting the coin that demonstrate that the coin
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index e378fc932..054df58db 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1350,7 +1350,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT:
{
unsigned int source_index;
- int ret;
+ enum GNUNET_DB_QueryStatus ret;
struct PERF_TALER_EXCHANGEDB_Data *data;
source_index = state->cmd[state->i].details.get_deposit.index_deposit;
@@ -1358,7 +1358,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
ret = state->plugin->have_deposit (state->plugin->cls,
state->session,
data->data.deposit);
- GNUNET_assert (GNUNET_SYSERR != ret);
+ GNUNET_assert (0 >= ret);
}
break;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index d56afed35..954fca389 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2698,11 +2698,11 @@ postgres_get_reserve_history (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
- * @return #GNUNET_YES if we know this operation,
- * #GNUNET_NO if this exact deposit is unknown to us
- * #GNUNET_SYSERR on DB error
+ * @return 1 if we know this operation,
+ * 0 if this exact deposit is unknown to us,
+ * otherwise transaction error status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2713,75 +2713,52 @@ postgres_have_deposit (void *cls,
GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
GNUNET_PQ_query_param_end
};
- PGresult *result;
-
- result = GNUNET_PQ_exec_prepared (session->conn,
- "get_deposit",
- params);
- if (PGRES_TUPLES_OK !=
- PQresultStatus (result))
- {
- BREAK_DB_ERR (result, session->conn);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if (0 == PQntuples (result))
- {
- PQclear (result);
- return GNUNET_NO;
- }
+ struct TALER_EXCHANGEDB_Deposit deposit2;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_amount ("amount_with_fee",
+ &deposit2.amount_with_fee),
+ GNUNET_PQ_result_spec_absolute_time ("timestamp",
+ &deposit2.timestamp),
+ GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+ &deposit2.refund_deadline),
+ GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+ &deposit2.wire_deadline),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &deposit2.h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+ &deposit2.h_wire),
+ GNUNET_PQ_result_spec_end
+ };
+ enum GNUNET_DB_QueryStatus qs;
+ qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+ "get_deposit",
+ params,
+ rs);
+ if (0 >= qs)
+ return qs;
/* Now we check that the other information in @a deposit
also matches, and if not report inconsistencies. */
- {
- struct TALER_EXCHANGEDB_Deposit deposit2;
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_amount ("amount_with_fee",
- &deposit2.amount_with_fee),
- GNUNET_PQ_result_spec_absolute_time ("timestamp",
- &deposit2.timestamp),
- GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
- &deposit2.refund_deadline),
- GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
- &deposit2.wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &deposit2.h_contract_terms),
- GNUNET_PQ_result_spec_auto_from_type ("h_wire",
- &deposit2.h_wire),
- GNUNET_PQ_result_spec_end
- };
- if (GNUNET_OK !=
- GNUNET_PQ_extract_result (result,
- rs,
- 0))
- {
- GNUNET_break (0);
- PQclear (result);
- return GNUNET_SYSERR;
- }
- if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
- &deposit2.amount_with_fee)) ||
- (deposit->timestamp.abs_value_us !=
- deposit2.timestamp.abs_value_us) ||
- (deposit->refund_deadline.abs_value_us !=
- deposit2.refund_deadline.abs_value_us) ||
- (0 != memcmp (&deposit->h_contract_terms,
- &deposit2.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) ||
- (0 != memcmp (&deposit->h_wire,
- &deposit2.h_wire,
- sizeof (struct GNUNET_HashCode))) )
- {
- /* Inconsistencies detected! Does not match! (We might want to
- expand the API with a 'get_deposit' function to return the
- original transaction details to be used for an error message
- in the future!) #3838 */
- PQclear (result);
- return GNUNET_NO;
- }
- }
- PQclear (result);
- return GNUNET_YES;
+ if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
+ &deposit2.amount_with_fee)) ||
+ (deposit->timestamp.abs_value_us !=
+ deposit2.timestamp.abs_value_us) ||
+ (deposit->refund_deadline.abs_value_us !=
+ deposit2.refund_deadline.abs_value_us) ||
+ (0 != memcmp (&deposit->h_contract_terms,
+ &deposit2.h_contract_terms,
+ sizeof (struct GNUNET_HashCode))) ||
+ (0 != memcmp (&deposit->h_wire,
+ &deposit2.h_wire,
+ sizeof (struct GNUNET_HashCode))) )
+ {
+ /* Inconsistencies detected! Does not match! (We might want to
+ expand the API with a 'get_deposit' function to return the
+ original transaction details to be used for an error message
+ in the future!) #3838 */
+ return 0; /* Counts as if the transaction was not there */
+ }
+ return 1;
}
@@ -3267,11 +3244,9 @@ postgres_insert_deposit (void *cls,
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param refund refund information to store
- * @return #GNUNET_OK on success
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
-static int
+static enum GNUNET_DB_QueryStatus
postgres_insert_refund (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund)
@@ -3289,9 +3264,9 @@ postgres_insert_refund (void *cls,
GNUNET_assert (GNUNET_YES ==
TALER_amount_cmp_currency (&refund->refund_amount,
&refund->refund_fee));
- return execute_prepared_non_select (session,
- "insert_refund",
- params);
+ return GNUNET_PQ_eval_prepared_non_select (session->conn,
+ "insert_refund",
+ params);
}
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 319b4dc11..ba47c2d3c 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1772,7 +1772,7 @@ run (void *cls)
plugin->insert_deposit (plugin->cls,
session,
&deposit));
- FAILIF (GNUNET_YES !=
+ FAILIF (1 !=
plugin->have_deposit (plugin->cls,
session,
&deposit));
@@ -1839,13 +1839,13 @@ run (void *cls)
result = 10;
deposit2 = deposit;
RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
- FAILIF (GNUNET_NO !=
+ FAILIF (0 !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
- FAILIF (GNUNET_NO !=
+ FAILIF (0 !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
@@ -1860,7 +1860,7 @@ run (void *cls)
refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
refund.refund_amount = deposit.amount_with_fee;
refund.refund_fee = fee_refund;
- FAILIF (GNUNET_OK !=
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refund (plugin->cls,
session,
&refund));
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index b5eaeea72..2cf553b17 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1293,11 +1293,11 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
- * @return #GNUNET_YES if we know this operation,
- * #GNUNET_NO if this exact deposit is unknown to us,
- * #GNUNET_SYSERR on DB error
+ * @return 1 if we know this operation,
+ * 0 if this exact deposit is unknown to us,
+ * otherwise transaction error status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*have_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1323,11 +1323,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param refund refund information to store
- * @return #GNUNET_OK on success,
- * #GNUNET_NO on transient error
- * #GNUNET_SYSERR on error
+ * @return query result status
*/
- int
+ enum GNUNET_DB_QueryStatus
(*insert_refund) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund);