incomplete work on fixing #5010 for /refresh/melt

This commit is contained in:
Christian Grothoff 2017-06-20 23:17:57 +02:00
parent 053096475f
commit 87e16541af
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
2 changed files with 252 additions and 333 deletions

View File

@ -31,94 +31,6 @@
#include "taler-exchange-httpd_keystate.h" #include "taler-exchange-httpd_keystate.h"
/**
* How often should we retry a transaction before giving up
* (for transactions resulting in serialization/dead locks only).
*/
#define MAX_TRANSACTION_COMMIT_RETRIES 3
/**
* Code to begin a transaction, must be inline as we define a block
* that ends with #COMMIT_TRANSACTION() within which we perform a number
* of retries. Note that this code may call "return" internally, so
* it must be called within a function where any cleanup will be done
* by the caller. Furthermore, the function's return value must
* match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
*
* @param session session handle
* @param connection connection handle
*/
#define START_TRANSACTION(session,connection) \
{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
unsigned int transaction_retries = 0; \
enum GNUNET_DB_QueryStatus transaction_commit_result; \
transaction_start_label: /* we will use goto for retries */ \
if (GNUNET_OK != \
TEH_plugin->start (TEH_plugin->cls, \
session)) \
{ \
GNUNET_break (0); \
return TEH_RESPONSE_reply_internal_db_error (connection, \
TALER_EC_DB_START_FAILED); \
}
/**
* Code to conclude a transaction, dual to #START_TRANSACTION(). Note
* that this code may call "return" internally, so it must be called
* within a function where any cleanup will be done by the caller.
* Furthermore, the function's return value must match that of a
* #TEH_RESPONSE_reply_internal_db_error() status code.
*
* @param session session handle
* @param connection connection handle
*/
#define COMMIT_TRANSACTION(session,connection) \
transaction_commit_result = \
TEH_plugin->commit (TEH_plugin->cls, \
session); \
if (GNUNET_DB_STATUS_HARD_ERROR == transaction_commit_result) \
{ \
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
return TEH_RESPONSE_reply_commit_error (connection, \
TALER_EC_DB_COMMIT_FAILED_HARD); \
} \
if (GNUNET_DB_STATUS_SOFT_ERROR == transaction_commit_result) \
{ \
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
goto transaction_start_label; \
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
transaction_retries, \
__FUNCTION__); \
return TEH_RESPONSE_reply_commit_error (connection, \
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
} \
} /* end of scope opened by BEGIN_TRANSACTION */
/**
* Code to include to retry a transaction, must only be used in between
* #START_TRANSACTION and #COMMIT_TRANSACTION.
*
* @param session session handle
* @param connection connection handle
*/
#define RETRY_TRANSACTION(session,connection) \
do { \
TEH_plugin->rollback (TEH_plugin->cls, \
session); \
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
goto transaction_start_label; \
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
transaction_retries, \
__FUNCTION__); \
return TEH_RESPONSE_reply_commit_error (connection, \
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
} while (0)
/** /**
* @brief Details about a melt operation of an individual coin. * @brief Details about a melt operation of an individual coin.
*/ */
@ -237,6 +149,56 @@ reply_refresh_melt_success (struct MHD_Connection *connection,
} }
/**
* Context for the /refresh/melt operation.
*/
struct RefreshMeltContext
{
/**
* Array of denominations of the fresh coins.
*/
struct TALER_DenominationPublicKey *denom_pubs;
/**
* Number of new coins to be generated in the melt.
* Size of the @e denom_pubs array.
*/
unsigned int num_newcoins;
/**
* Details about the coin to be melted.
*/
struct TEH_DB_MeltDetails coin_melt_details;
/**
* Set to the session hash once the @e hash_context has finished.
*/
struct GNUNET_HashCode session_hash;
/**
* Hash operation used to calculate the session hash.
*/
struct GNUNET_HashContext *hash_context;
/**
* Committments to the blinded envelopes for the fresh coins.
*/
struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
/**
* Commmittments to the transfer public keys.
*/
struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
/**
* Initialized during #refresh_melt_transaction().
*/
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
};
/** /**
* Parse coin melt requests from a JSON object and write them to * Parse coin melt requests from a JSON object and write them to
* the database. * the database.
@ -341,136 +303,102 @@ refresh_check_melt (struct MHD_Connection *connection,
* required value left and if so, store that they have been * required value left and if so, store that they have been
* melted and confirm the melting operation to the client. * melted and confirm the melting operation to the client.
* *
* @param connection the MHD connection to handle * If it returns a non-error code, the transaction logic MUST
* @param session_hash hash code of the session the coins are melted into * NOT queue a MHD response. IF it returns an hard error, the
* @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array * transaction logic MUST queue a MHD response and set @a mhd_ret. IF
* @param denom_pubs public keys of the coins we want to withdraw in the end * it returns the soft error code, the function MAY be called again to
* @param coin_melt_detail signature and (residual) value of the respective coin should be melted * retry and MUST not queue a MHD response.
* @param commit_coin 2d array of coin commitments (what the exchange is to sign *
* once the "/refres/reveal" of cut and choose is done), * @param cls our `struct RefreshMeltContext`
* x-dimension must be #TALER_CNC_KAPPA * @param connection MHD request which triggered the transaction
* @param transfer_pubs array of transfer public keys (what the exchange is * @param session database session to use
* to return via "/refresh/link" to enable linkage in the * @param[out] mhd_ret set to MHD response status for @a connection,
* future) of length #TALER_CNC_KAPPA * if transaction failed (!)
* @return MHD result code * @return transaction status
*/ */
static int static enum GNUNET_DB_QueryStatus
execute_refresh_melt (struct MHD_Connection *connection, refresh_melt_transaction (void *cls,
const struct GNUNET_HashCode *session_hash, struct MHD_Connection *connection,
unsigned int num_new_denoms, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_DenominationPublicKey *denom_pubs, int *mhd_ret)
const struct TEH_DB_MeltDetails *coin_melt_detail,
struct TALER_EXCHANGEDB_RefreshCommitCoin *const* commit_coin,
const struct TALER_TransferPublicKeyP *transfer_pubs)
{ {
struct RefreshMeltContext *rmc = cls;
struct TEH_KS_StateHandle *key_state; struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_RefreshSession refresh_session; enum GNUNET_DB_QueryStatus qs;
struct TALER_EXCHANGEDB_Session *session;
int res; int res;
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
{
GNUNET_break (0);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_DB_SETUP_FAILED);
}
START_TRANSACTION (session, connection);
res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
session, session,
session_hash, &rmc->session_hash,
&refresh_session); &rmc->refresh_session);
if (GNUNET_YES == res) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{ {
TEH_plugin->rollback (TEH_plugin->cls, *mhd_ret = reply_refresh_melt_success (connection,
session); &rmc->session_hash,
res = reply_refresh_melt_success (connection, rmc->refresh_session.noreveal_index);
session_hash, return GNUNET_DB_STATUS_HARD_ERROR;
refresh_session.noreveal_index);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
} }
if (GNUNET_SYSERR == res) if (0 > qs)
{ {
TEH_plugin->rollback (TEH_plugin->cls, if (GNUNET_DB_STATUS_HARD_ERROR == qs)
session); *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR); TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
return qs;
} }
/* store 'global' session data */ /* store 'global' session data */
refresh_session.num_newcoins = num_new_denoms; rmc->refresh_session.num_newcoins = rmc->num_newcoins;
refresh_session.noreveal_index rmc->refresh_session.noreveal_index
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
TALER_CNC_KAPPA); TALER_CNC_KAPPA);
key_state = TEH_KS_acquire (); key_state = TEH_KS_acquire ();
if (GNUNET_OK != if (GNUNET_OK !=
(res = refresh_check_melt (connection, (res = refresh_check_melt (connection,
session, session,
key_state, key_state,
session_hash, &rmc->session_hash,
coin_melt_detail, &rmc->coin_melt_details,
&refresh_session.melt))) &rmc->refresh_session.melt)))
{ {
TEH_KS_release (key_state); TEH_KS_release (key_state);
TEH_plugin->rollback (TEH_plugin->cls, *mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
session); return GNUNET_DB_STATUS_HARD_ERROR;
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
} }
TEH_KS_release (key_state); TEH_KS_release (key_state);
if (GNUNET_OK != if ( (0 >=
(res = TEH_plugin->create_refresh_session (TEH_plugin->cls, (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls,
session, session,
session_hash, &rmc->session_hash,
&refresh_session))) &rmc->refresh_session))) ||
(0 >=
(qs = TEH_plugin->insert_refresh_order (TEH_plugin->cls,
session,
&rmc->session_hash,
rmc->num_newcoins,
rmc->denom_pubs))) ||
(0 >=
(qs = TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
session,
&rmc->session_hash,
rmc->num_newcoins,
rmc->commit_coin[rmc->refresh_session.noreveal_index]))) ||
(0 >=
(qs = TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
session,
&rmc->session_hash,
&rmc->transfer_pub[rmc->refresh_session.noreveal_index]))) )
{ {
TEH_plugin->rollback (TEH_plugin->cls, if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
session); {
return TEH_RESPONSE_reply_internal_db_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR); TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
return GNUNET_DB_STATUS_HARD_ERROR;
} }
return qs;
/* store requested new denominations */
if (GNUNET_OK !=
TEH_plugin->insert_refresh_order (TEH_plugin->cls,
session,
session_hash,
num_new_denoms,
denom_pubs))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
} }
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
if (GNUNET_OK !=
TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
session,
session_hash,
num_new_denoms,
commit_coin[refresh_session.noreveal_index]))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
}
if (GNUNET_OK !=
TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
session,
session_hash,
&transfer_pubs[refresh_session.noreveal_index]))
{
TEH_plugin->rollback (TEH_plugin->cls,
session);
return TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
}
COMMIT_TRANSACTION (session, connection);
return reply_refresh_melt_success (connection,
session_hash,
refresh_session.noreveal_index);
} }
@ -480,25 +408,14 @@ execute_refresh_melt (struct MHD_Connection *connection,
* and then hand things of to execute the melt operation. * and then hand things of to execute the melt operation.
* *
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param num_new_denoms number of coins to be created, size of y-dimension of @a commit_link array * @param[out] mhd_ret set on failure to return value for MHD
* @param denom_pubs array of @a num_new_denoms keys * @param rmc information about the melt to process
* @param coin_melt_details melting details
* @param session_hash hash over the data that the client commits to
* @param commit_coin 2d array of coin commitments (what the exchange is to sign
* once the "/refres/reveal" of cut and choose is done)
* @param transfer_pubs array of transfer public keys (which the exchange is
* to return via "/refresh/link" to enable linkage in the
* future) of length #TALER_CNC_KAPPA
* @return MHD result code * @return MHD result code
*/ */
static int static int
handle_refresh_melt_binary (struct MHD_Connection *connection, refresh_melt_prepare (struct MHD_Connection *connection,
unsigned int num_new_denoms, int *mhd_ret,
const struct TALER_DenominationPublicKey *denom_pubs, struct RefreshMeltContext *rmc)
const struct TEH_DB_MeltDetails *coin_melt_details,
const struct GNUNET_HashCode *session_hash,
struct TALER_EXCHANGEDB_RefreshCommitCoin *const* commit_coin,
const struct TALER_TransferPublicKeyP *transfer_pubs)
{ {
struct TEH_KS_StateHandle *key_state; struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
@ -512,24 +429,25 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"/refresh/melt request for session %s\n", "/refresh/melt request for session %s\n",
GNUNET_h2s (session_hash)); GNUNET_h2s (&rmc->session_hash));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (TEH_exchange_currency_string, TALER_amount_get_zero (TEH_exchange_currency_string,
&total_cost)); &total_cost));
key_state = TEH_KS_acquire (); key_state = TEH_KS_acquire ();
for (unsigned int i=0;i<num_new_denoms;i++) for (unsigned int i=0;i<rmc->num_newcoins;i++)
{ {
dk = TEH_KS_denomination_key_lookup (key_state, dk = TEH_KS_denomination_key_lookup (key_state,
&denom_pubs[i], &rmc->denom_pubs[i],
TEH_KS_DKU_WITHDRAW); TEH_KS_DKU_WITHDRAW);
if (NULL == dk) if (NULL == dk)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_KS_release (key_state); TEH_KS_release (key_state);
return TEH_RESPONSE_reply_arg_invalid (connection, *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND, TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
"new_denoms"); "new_denoms");
return GNUNET_SYSERR;
} }
dki = &dk->issue; dki = &dk->issue;
TALER_amount_ntoh (&value, TALER_amount_ntoh (&value,
@ -547,35 +465,38 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_KS_release (key_state); TEH_KS_release (key_state);
return TEH_RESPONSE_reply_internal_error (connection, *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW, TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
"cost calculation failure"); "cost calculation failure");
return GNUNET_SYSERR;
} }
} }
dk = TEH_KS_denomination_key_lookup (key_state, dk = TEH_KS_denomination_key_lookup (key_state,
&coin_melt_details->coin_info.denom_pub, &rmc->coin_melt_details.coin_info.denom_pub,
TEH_KS_DKU_DEPOSIT); TEH_KS_DKU_DEPOSIT);
if (NULL == dk) if (NULL == dk)
{ {
GNUNET_break (0); GNUNET_break (0);
return TEH_RESPONSE_reply_arg_unknown (connection, *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND, TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
"denom_pub"); "denom_pub");
return GNUNET_SYSERR;
} }
dki = &dk->issue; dki = &dk->issue;
TALER_amount_ntoh (&fee_melt, TALER_amount_ntoh (&fee_melt,
&dki->properties.fee_refresh); &dki->properties.fee_refresh);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_subtract (&total_melt, TALER_amount_subtract (&total_melt,
&coin_melt_details->melt_amount_with_fee, &rmc->coin_melt_details.melt_amount_with_fee,
&fee_melt)) &fee_melt))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_KS_release (key_state); TEH_KS_release (key_state);
return TEH_RESPONSE_reply_external_error (connection, *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION, TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
"Melt contribution below melting fee"); "Melt contribution below melting fee");
return GNUNET_SYSERR;
} }
TEH_KS_release (key_state); TEH_KS_release (key_state);
if (0 != if (0 !=
@ -585,19 +506,17 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
GNUNET_break_op (0); GNUNET_break_op (0);
/* We require total value of coins being melted and /* We require total value of coins being melted and
total value of coins being generated to match! */ total value of coins being generated to match! */
return TEH_RESPONSE_reply_json_pack (connection, *mhd_ret = TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
"{s:s, s:I}", "{s:s, s:I}",
"error", "value mismatch", "error", "value mismatch",
"code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH); "code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
return GNUNET_SYSERR;
} }
return execute_refresh_melt (connection, return TEH_DB_run_transaction (connection,
session_hash, mhd_ret,
num_new_denoms, &refresh_melt_transaction,
denom_pubs, rmc);
coin_melt_details,
commit_coin,
transfer_pubs);
} }
@ -764,6 +683,33 @@ free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
} }
/**
* Cleanup state kept in the @a rmc.
*
* @param rmc state to clean up; does not free @a rmc itself
*/
static void
cleanup_rmc (struct RefreshMeltContext *rmc)
{
free_commit_coins (rmc->commit_coin,
TALER_CNC_KAPPA,
rmc->num_newcoins);
if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key);
if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (rmc->coin_melt_details.coin_info.denom_sig.rsa_signature);
if (NULL != rmc->denom_pubs)
{
for (unsigned int j=0;j<rmc->num_newcoins;j++)
if (NULL != rmc->denom_pubs[j].rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (rmc->denom_pubs[j].rsa_public_key);
GNUNET_free (rmc->denom_pubs);
}
if (NULL != rmc->hash_context)
GNUNET_CRYPTO_hash_context_abort (rmc->hash_context);
}
/** /**
* Handle a "/refresh/melt" request after the first parsing has happened. * Handle a "/refresh/melt" request after the first parsing has happened.
* We now need to validate the coins being melted and the session signature * We now need to validate the coins being melted and the session signature
@ -786,22 +732,20 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
const json_t *coin_evs) const json_t *coin_evs)
{ {
int res; int res;
struct TALER_DenominationPublicKey *denom_pubs; int mhd_ret;
unsigned int num_newcoins; struct RefreshMeltContext rmc;
struct TEH_DB_MeltDetails coin_melt_details;
struct GNUNET_HashCode session_hash;
struct GNUNET_HashContext *hash_context;
struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
memset (&rmc,
0,
sizeof (rmc));
/* For the signature check, we hash most of the inputs together /* For the signature check, we hash most of the inputs together
(except for the signatures on the coins). */ (except for the signatures on the coins). */
hash_context = GNUNET_CRYPTO_hash_context_start (); rmc.hash_context = GNUNET_CRYPTO_hash_context_start ();
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
{ {
struct GNUNET_JSON_Specification trans_spec[] = { struct GNUNET_JSON_Specification trans_spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL, &transfer_pub[i]), GNUNET_JSON_spec_fixed_auto (NULL, &rmc.transfer_pub[i]),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -812,40 +756,43 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
if (GNUNET_OK != res) if (GNUNET_OK != res)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
goto cleanup_hc; cleanup_rmc (&rmc);
return mhd_ret;
} }
GNUNET_CRYPTO_hash_context_read (hash_context, GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
&transfer_pub[i], &rmc.transfer_pub[i],
sizeof (struct TALER_TransferPublicKeyP)); sizeof (struct TALER_TransferPublicKeyP));
} }
num_newcoins = json_array_size (new_denoms); rmc.num_newcoins = json_array_size (new_denoms);
denom_pubs = GNUNET_new_array (num_newcoins, rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins,
struct TALER_DenominationPublicKey); struct TALER_DenominationPublicKey);
for (unsigned int i=0;i<num_newcoins;i++) for (unsigned int i=0;i<rmc.num_newcoins;i++)
{ {
char *buf; char *buf;
size_t buf_size; size_t buf_size;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_denomination_public_key (NULL, TALER_JSON_spec_denomination_public_key (NULL,
&denom_pubs[i]), &rmc.denom_pubs[i]),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
res = TEH_PARSE_json_array (connection, res = TEH_PARSE_json_array (connection,
new_denoms, new_denoms,
spec, spec,
i, -1); i,
-1);
if (GNUNET_OK != res) if (GNUNET_OK != res)
{ {
res = (GNUNET_NO == res) ? MHD_YES : MHD_NO; mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
goto cleanup_denoms; cleanup_rmc (&rmc);
return mhd_ret;
} }
buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs[i].rsa_public_key, buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rmc.denom_pubs[i].rsa_public_key,
&buf); &buf);
GNUNET_CRYPTO_hash_context_read (hash_context, GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
buf, buf,
buf_size); buf_size);
GNUNET_free (buf); GNUNET_free (buf);
@ -857,35 +804,33 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
res = get_coin_public_info (connection, res = get_coin_public_info (connection,
melt_coin, melt_coin,
&coin_melt_details); &rmc.coin_melt_details);
if (GNUNET_OK != res) if (GNUNET_OK != res)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
res = (GNUNET_NO == res) ? MHD_YES : MHD_NO; mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
goto cleanup_melt_details; cleanup_rmc (&rmc);
return mhd_ret;
} }
TALER_amount_hton (&melt_amount, TALER_amount_hton (&melt_amount,
&coin_melt_details.melt_amount_with_fee); &rmc.coin_melt_details.melt_amount_with_fee);
GNUNET_CRYPTO_hash_context_read (hash_context, GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
&coin_melt_details.coin_info.coin_pub, &rmc.coin_melt_details.coin_info.coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP)); sizeof (struct TALER_CoinSpendPublicKeyP));
GNUNET_CRYPTO_hash_context_read (hash_context, GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
&melt_amount, &melt_amount,
sizeof (struct TALER_AmountNBO)); sizeof (struct TALER_AmountNBO));
} }
/* parse JSON arrays into binary arrays and hash everything /* parse JSON arrays into binary arrays and hash everything
together for the signature check */ together for the signature check */
memset (commit_coin,
0,
sizeof (commit_coin));
for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
{ {
commit_coin[i] = GNUNET_new_array (num_newcoins, rmc.commit_coin[i] = GNUNET_new_array (rmc.num_newcoins,
struct TALER_EXCHANGEDB_RefreshCommitCoin); struct TALER_EXCHANGEDB_RefreshCommitCoin);
for (unsigned int j = 0; j < num_newcoins; j++) for (unsigned int j = 0; j < rmc.num_newcoins; j++)
{ {
struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &commit_coin[i][j]; struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &rmc.commit_coin[i][j];
struct GNUNET_JSON_Specification coin_spec[] = { struct GNUNET_JSON_Specification coin_spec[] = {
GNUNET_JSON_spec_varsize (NULL, GNUNET_JSON_spec_varsize (NULL,
(void **) &rcc->coin_ev, (void **) &rcc->coin_ev,
@ -896,63 +841,52 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
res = TEH_PARSE_json_array (connection, res = TEH_PARSE_json_array (connection,
coin_evs, coin_evs,
coin_spec, coin_spec,
i, j, -1); i,
j,
-1);
if (GNUNET_OK != res) if (GNUNET_OK != res)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
goto cleanup; cleanup_rmc (&rmc);
return mhd_ret;
} }
GNUNET_CRYPTO_hash_context_read (hash_context, GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
rcc->coin_ev, rcc->coin_ev,
rcc->coin_ev_size); rcc->coin_ev_size);
} }
} }
GNUNET_CRYPTO_hash_context_finish (hash_context, GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
&session_hash); &rmc.session_hash);
hash_context = NULL; rmc.hash_context = NULL;
/* verify signature on coins to melt */ /* verify signature on coins to melt */
res = verify_coin_public_info (connection, res = verify_coin_public_info (connection,
&session_hash, &rmc.session_hash,
&coin_melt_details); &rmc.coin_melt_details);
if (GNUNET_OK != res) if (GNUNET_OK != res)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
res = (GNUNET_NO == res) ? MHD_YES : MHD_NO; mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
goto cleanup; cleanup_rmc (&rmc);
return mhd_ret;
} }
/* execute commit */ /* prepare commit */
res = handle_refresh_melt_binary (connection, if (GNUNET_OK !=
num_newcoins, refresh_melt_prepare (connection,
denom_pubs, &mhd_ret,
&coin_melt_details, &rmc))
&session_hash,
commit_coin,
transfer_pub);
cleanup:
free_commit_coins (commit_coin,
TALER_CNC_KAPPA,
num_newcoins);
cleanup_melt_details:
if (NULL != coin_melt_details.coin_info.denom_pub.rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (coin_melt_details.coin_info.denom_pub.rsa_public_key);
if (NULL != coin_melt_details.coin_info.denom_sig.rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (coin_melt_details.coin_info.denom_sig.rsa_signature);
cleanup_denoms:
if (NULL != denom_pubs)
{ {
for (unsigned int j=0;j<num_newcoins;j++) cleanup_rmc (&rmc);
if (NULL != denom_pubs[j].rsa_public_key) return mhd_ret;
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
GNUNET_free (denom_pubs);
} }
cleanup_hc: mhd_ret = reply_refresh_melt_success (connection,
if (NULL != hash_context) &rmc.session_hash,
GNUNET_CRYPTO_hash_context_abort (hash_context); rmc.refresh_session.noreveal_index);
return res; cleanup_rmc (&rmc);
return mhd_ret;
} }
@ -997,7 +931,8 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
&root); &root);
if (GNUNET_SYSERR == res) if (GNUNET_SYSERR == res)
return MHD_NO; return MHD_NO;
if ( (GNUNET_NO == res) || (NULL == root) ) if ( (GNUNET_NO == res) ||
(NULL == root) )
return MHD_YES; return MHD_YES;
res = TEH_PARSE_json_data (connection, res = TEH_PARSE_json_data (connection,

View File

@ -440,14 +440,6 @@ enum TALER_ErrorCode
*/ */
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304, TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304,
/**
* The exchange failed to store refresh order data in the
* database.
* This response is provided with HTTP status code
* MHD_HTTP_INTERNAL_ERROR.
*/
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR = 1305,
/** /**
* The exchange failed to store commit data in the * The exchange failed to store commit data in the
* database. * database.
@ -456,14 +448,6 @@ enum TALER_ErrorCode
*/ */
TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306, TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306,
/**
* The exchange failed to store transfer keys in the
* database.
* This response is provided with HTTP status code
* MHD_HTTP_INTERNAL_ERROR.
*/
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR = 1307,
/** /**
* The exchange is unaware of the denomination key that was * The exchange is unaware of the denomination key that was
* requested for one of the fresh coins. This response is provided * requested for one of the fresh coins. This response is provided