aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/exchange/taler-exchange-httpd_db.c167
-rw-r--r--src/exchange/taler-exchange-httpd_db.h26
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c154
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c186
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c65
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c3
-rw-r--r--src/lib/exchange_api_deposit.c2
-rw-r--r--src/lib/exchange_api_melt.c8
8 files changed, 294 insertions, 317 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index c091f994d..383a7b297 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
@@ -29,6 +29,55 @@
/**
+ * 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 83302235d..60885dbd1 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -42,6 +42,32 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo *coin,
/**
+ * 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
* NOT queue a MHD response. IF it returns an hard error, the
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index d549a1fa6..43acad44b 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 c00eb8afe..ab7bed295 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -34,56 +34,6 @@
/**
- * 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.
*
* @param connection the connection to send the response to
@@ -166,127 +116,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
* refresh_session_pub. Check that the coins all have the required
@@ -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 08587a516..6c1766feb 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -106,6 +106,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.
*/
const struct TEH_DenominationKey **dks;
@@ -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 33383df59..73a03e751 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 8b93f56ef..3fd3353b2 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 f3032e8b2..6ef9f4cbc 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,