diff --git a/contrib/gana b/contrib/gana index b7320181c..b12314034 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 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 @@ -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 83302235d..60885dbd1 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 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_SUCCESS_NO_RESULTS == 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 */)) - { - 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 @@ -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 08587a516..6c1766feb 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; imelt.session.noreveal_index) continue; /* This offset is special: not allocated! */ for (unsigned int j = 0; jnum_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; inum_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; igamma_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,