optimize /deposit logic to minimize serialization failures (presumably)

This commit is contained in:
Christian Grothoff 2021-12-08 20:33:14 +01:00
parent 21951eacc2
commit e0700ad916
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
9 changed files with 295 additions and 318 deletions

@ -1 +1 @@
Subproject commit b7320181c5e0d95c6f2e2a9e5c53dce0bc1a35a8
Subproject commit b123140349c3e3b300878d2e35cea1553c9a381d

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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; 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);

View File

@ -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(). */

View File

@ -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,

View File

@ -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,