address #5010 for /refresh/melt

This commit is contained in:
Christian Grothoff 2017-06-22 11:49:40 +02:00
parent 87e16541af
commit fbff951e7d
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 205 additions and 273 deletions

View File

@ -83,8 +83,8 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_Amount coin_value, struct TALER_Amount coin_value,
struct TALER_EXCHANGEDB_TransactionList *tl, struct TALER_EXCHANGEDB_TransactionList *tl,
struct TALER_Amount requested, const struct TALER_Amount *requested,
struct TALER_Amount residual) const struct TALER_Amount *residual)
{ {
json_t *history; json_t *history;
@ -104,9 +104,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
"original_value", "original_value",
TALER_JSON_from_amount (&coin_value), TALER_JSON_from_amount (&coin_value),
"residual_value", "residual_value",
TALER_JSON_from_amount (&residual), TALER_JSON_from_amount (residual),
"requested_value", "requested_value",
TALER_JSON_from_amount (&requested), TALER_JSON_from_amount (requested),
"history", "history",
history); history);
} }
@ -155,6 +155,17 @@ reply_refresh_melt_success (struct MHD_Connection *connection,
struct RefreshMeltContext struct RefreshMeltContext
{ {
/**
* Key state that can be used to lookup keys.
*/
struct TEH_KS_StateHandle *key_state;
/**
* Information about the denomination key of the coin being
* melted.
*/
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
/** /**
* Array of denominations of the fresh coins. * Array of denominations of the fresh coins.
*/ */
@ -205,51 +216,39 @@ struct RefreshMeltContext
* *
* @param connection the connection to send errors to * @param connection the connection to send errors to
* @param session the database connection * @param session the database connection
* @param key_state the exchange's key state * @param[in,out] rmc melt context
* @param session_hash hash identifying the refresh session * @param[out] mhd_ret status code to return to MHD on hard error
* @param coin_details details about the coin being melted * @return transaction status code
* @param[out] meltp on success, set to melt details
* @return #GNUNET_OK on success,
* #GNUNET_NO if an error message was generated,
* #GNUNET_SYSERR on internal errors (no response generated)
*/ */
static int static enum GNUNET_DB_QueryStatus
refresh_check_melt (struct MHD_Connection *connection, refresh_check_melt (struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TEH_KS_StateHandle *key_state, struct RefreshMeltContext *rmc,
const struct GNUNET_HashCode *session_hash, int *mhd_ret)
const struct TEH_DB_MeltDetails *coin_details,
struct TALER_EXCHANGEDB_RefreshMelt *meltp)
{ {
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tl;
struct TALER_EXCHANGEDB_RefreshMelt *meltp = &rmc->refresh_session.melt;
struct TALER_Amount coin_value; struct TALER_Amount coin_value;
struct TALER_Amount coin_residual; struct TALER_Amount coin_residual;
struct TALER_Amount spent; struct TALER_Amount spent;
int res;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
dk = TEH_KS_denomination_key_lookup (key_state,
&coin_details->coin_info.denom_pub,
TEH_KS_DKU_DEPOSIT);
if (NULL == dk)
return (MHD_YES ==
TEH_RESPONSE_reply_internal_error (connection,
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
"denomination key no longer available while executing transaction"))
? GNUNET_NO : GNUNET_SYSERR;
dki = &dk->issue;
TALER_amount_ntoh (&coin_value, TALER_amount_ntoh (&coin_value,
&dki->properties.value); &rmc->dki->issue.properties.value);
/* fee for THIS transaction; the melt amount includes the fee! */ /* fee for THIS transaction; the melt amount includes the fee! */
spent = coin_details->melt_amount_with_fee; spent = rmc->coin_melt_details.melt_amount_with_fee;
/* add historic transaction costs of this coin */ /* add historic transaction costs of this coin */
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
session, session,
&coin_details->coin_info.coin_pub, &rmc->coin_melt_details.coin_info.coin_pub,
&tl); &tl);
(void) qs; /* FIXME #5010 */ if (0 > qs)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
return qs;
}
if (GNUNET_OK != if (GNUNET_OK !=
TEH_DB_calculate_transaction_list_totals (tl, TEH_DB_calculate_transaction_list_totals (tl,
&spent, &spent,
@ -258,10 +257,9 @@ refresh_check_melt (struct MHD_Connection *connection,
GNUNET_break (0); GNUNET_break (0);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl); tl);
return (MHD_YES == *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TEH_RESPONSE_reply_internal_db_error (connection, TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED);
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED)) return GNUNET_DB_STATUS_HARD_ERROR;
? GNUNET_NO : GNUNET_SYSERR;
} }
/* Refuse to refresh when the coin's value is insufficient /* Refuse to refresh when the coin's value is insufficient
for the cost of all transactions. */ for the cost of all transactions. */
@ -271,43 +269,41 @@ refresh_check_melt (struct MHD_Connection *connection,
GNUNET_assert (GNUNET_SYSERR != GNUNET_assert (GNUNET_SYSERR !=
TALER_amount_subtract (&coin_residual, TALER_amount_subtract (&coin_residual,
&spent, &spent,
&coin_details->melt_amount_with_fee)); &rmc->coin_melt_details.melt_amount_with_fee));
res = (MHD_YES == *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
reply_refresh_melt_insufficient_funds (connection, &rmc->coin_melt_details.coin_info.coin_pub,
&coin_details->coin_info.coin_pub,
coin_value, coin_value,
tl, tl,
coin_details->melt_amount_with_fee, &rmc->coin_melt_details.melt_amount_with_fee,
coin_residual)) &coin_residual);
? GNUNET_NO : GNUNET_SYSERR;
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl); tl);
return res; return GNUNET_DB_STATUS_HARD_ERROR;
} }
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl); tl);
meltp->coin = coin_details->coin_info; meltp->coin = rmc->coin_melt_details.coin_info;
meltp->coin_sig = coin_details->melt_sig; meltp->coin_sig = rmc->coin_melt_details.melt_sig;
meltp->session_hash = *session_hash; meltp->session_hash = rmc->session_hash;
meltp->amount_with_fee = coin_details->melt_amount_with_fee; meltp->amount_with_fee = rmc->coin_melt_details.melt_amount_with_fee;
meltp->melt_fee = coin_details->melt_fee; meltp->melt_fee = rmc->coin_melt_details.melt_fee;
return GNUNET_OK; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }
/** /**
* Execute a "/refresh/melt". We have been given a list of valid * Execute a "/refresh/melt". We have been given a list of valid
* coins and a request to melt them into the given * coins and a request to melt them into the given @a
* @a refresh_session_pub. Check that the coins all have the * refresh_session_pub. Check that the coins all have the required
* required value left and if so, store that they have been * value left and if so, store that they have been melted and confirm
* melted and confirm the melting operation to the client. * the melting operation to the client.
* *
* If it returns a non-error code, the transaction logic MUST * If it returns a non-error code, the transaction logic MUST NOT
* NOT queue a MHD response. IF it returns an hard error, the * queue a MHD response. IF it returns an hard error, the transaction
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF * logic MUST queue a MHD response and set @a mhd_ret. If it returns
* it returns the soft error code, the function MAY be called again to * the soft error code, the function MAY be called again to retry and
* retry and MUST not queue a MHD response. * MUST not queue a MHD response.
* *
* @param cls our `struct RefreshMeltContext` * @param cls our `struct RefreshMeltContext`
* @param connection MHD request which triggered the transaction * @param connection MHD request which triggered the transaction
@ -323,9 +319,7 @@ refresh_melt_transaction (void *cls,
int *mhd_ret) int *mhd_ret)
{ {
struct RefreshMeltContext *rmc = cls; struct RefreshMeltContext *rmc = cls;
struct TEH_KS_StateHandle *key_state;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
int res;
qs = TEH_plugin->get_refresh_session (TEH_plugin->cls, qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
session, session,
@ -352,20 +346,12 @@ refresh_melt_transaction (void *cls,
= 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 (); qs = refresh_check_melt (connection,
if (GNUNET_OK !=
(res = refresh_check_melt (connection,
session, session,
key_state, rmc,
&rmc->session_hash, mhd_ret);
&rmc->coin_melt_details, if (0 > qs)
&rmc->refresh_session.melt))) return qs;
{
TEH_KS_release (key_state);
*mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
return GNUNET_DB_STATUS_HARD_ERROR;
}
TEH_KS_release (key_state);
if ( (0 >= if ( (0 >=
(qs = TEH_plugin->create_refresh_session (TEH_plugin->cls, (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls,
@ -403,9 +389,10 @@ refresh_melt_transaction (void *cls,
/** /**
* Handle a "/refresh/melt" request after the main JSON parsing has happened. * Handle a "/refresh/melt" request after the main JSON parsing has
* We now need to validate the coins being melted and the session signature * happened. We now need to validate the coins being melted and the
* and then hand things of to execute the melt operation. * session signature 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[out] mhd_ret set on failure to return value for MHD * @param[out] mhd_ret set on failure to return value for MHD
@ -417,7 +404,6 @@ refresh_melt_prepare (struct MHD_Connection *connection,
int *mhd_ret, int *mhd_ret,
struct RefreshMeltContext *rmc) struct RefreshMeltContext *rmc)
{ {
struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki; struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
struct TALER_Amount cost; struct TALER_Amount cost;
@ -434,16 +420,14 @@ refresh_melt_prepare (struct MHD_Connection *connection,
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 ();
for (unsigned int i=0;i<rmc->num_newcoins;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 (rmc->key_state,
&rmc->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);
*mhd_ret = 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");
@ -464,7 +448,6 @@ refresh_melt_prepare (struct MHD_Connection *connection,
&total_cost)) ) &total_cost)) )
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_KS_release (key_state);
*mhd_ret = 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");
@ -472,18 +455,7 @@ refresh_melt_prepare (struct MHD_Connection *connection,
} }
} }
dk = TEH_KS_denomination_key_lookup (key_state, dki = &rmc->dki->issue;
&rmc->coin_melt_details.coin_info.denom_pub,
TEH_KS_DKU_DEPOSIT);
if (NULL == dk)
{
GNUNET_break (0);
*mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
"denom_pub");
return GNUNET_SYSERR;
}
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 !=
@ -492,13 +464,11 @@ refresh_melt_prepare (struct MHD_Connection *connection,
&fee_melt)) &fee_melt))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_KS_release (key_state);
*mhd_ret = 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; return GNUNET_SYSERR;
} }
TEH_KS_release (key_state);
if (0 != if (0 !=
TALER_amount_cmp (&total_cost, TALER_amount_cmp (&total_cost,
&total_melt)) &total_melt))
@ -579,87 +549,6 @@ get_coin_public_info (struct MHD_Connection *connection,
} }
/**
* Verify that the signature shows that this coin is to be melted into
* the given @a session_hash melting session, and that this is a valid
* coin (we know the denomination key and the signature on it is
* valid). Essentially, this does all of the per-coin checks that can
* be done before the transaction starts.
*
* @param connection the connection to send error responses to
* @param session_hash hash over refresh session the coin is melted into
* @param[in,out] melt_detail details about the coin's melting permission,
* the `melt_fee` is updated
* @return #GNUNET_YES if coin public info in JSON was valid
* #GNUNET_NO JSON was invalid, response was generated
* #GNUNET_SYSERR on internal error
*/
static int
verify_coin_public_info (struct MHD_Connection *connection,
const struct GNUNET_HashCode *session_hash,
struct TEH_DB_MeltDetails *melt_detail)
{
struct TALER_RefreshMeltCoinAffirmationPS body;
struct TEH_KS_StateHandle *key_state;
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct TALER_Amount fee_refresh;
/* FIXME: we lookup the dki twice during /refresh/melt.
This should be avoided. */
key_state = TEH_KS_acquire ();
dki = TEH_KS_denomination_key_lookup (key_state,
&melt_detail->coin_info.denom_pub,
TEH_KS_DKU_DEPOSIT);
if (NULL == dki)
{
TEH_KS_release (key_state);
TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
return TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
"denom_pub");
}
TALER_amount_ntoh (&fee_refresh,
&dki->issue.properties.fee_refresh);
melt_detail->melt_fee = fee_refresh;
body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
body.session_hash = *session_hash;
TALER_amount_hton (&body.amount_with_fee,
&melt_detail->melt_amount_with_fee);
TALER_amount_hton (&body.melt_fee,
&fee_refresh);
body.coin_pub = melt_detail->coin_info.coin_pub;
if (TALER_amount_cmp (&fee_refresh,
&melt_detail->melt_amount_with_fee) > 0)
{
GNUNET_break_op (0);
TEH_KS_release (key_state);
return (MHD_YES ==
TEH_RESPONSE_reply_external_error (connection,
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
"melt amount smaller than melting fee"))
? GNUNET_NO : GNUNET_SYSERR;
}
TEH_KS_release (key_state);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&body.purpose,
&melt_detail->melt_sig.eddsa_signature,
&melt_detail->coin_info.coin_pub.eddsa_pub))
{
GNUNET_break_op (0);
if (MHD_YES !=
TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
"confirm_sig"))
return GNUNET_SYSERR;
return GNUNET_NO;
}
return GNUNET_OK;
}
/** /**
* Release memory from the @a commit_coin array. * Release memory from the @a commit_coin array.
* *
@ -679,6 +568,7 @@ free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
for (unsigned int j=0;j<num_new_coins;j++) for (unsigned int j=0;j<num_new_coins;j++)
GNUNET_free_non_null (commit_coin[i][j].coin_ev); GNUNET_free_non_null (commit_coin[i][j].coin_ev);
GNUNET_free (commit_coin[i]); GNUNET_free (commit_coin[i]);
commit_coin[i] = NULL;
} }
} }
@ -695,27 +585,42 @@ cleanup_rmc (struct RefreshMeltContext *rmc)
TALER_CNC_KAPPA, TALER_CNC_KAPPA,
rmc->num_newcoins); rmc->num_newcoins);
if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key) 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); GNUNET_CRYPTO_rsa_public_key_free (rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key);
rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key = NULL;
}
if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature) 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); GNUNET_CRYPTO_rsa_signature_free (rmc->coin_melt_details.coin_info.denom_sig.rsa_signature);
rmc->coin_melt_details.coin_info.denom_sig.rsa_signature = NULL;
}
if (NULL != rmc->denom_pubs) if (NULL != rmc->denom_pubs)
{ {
for (unsigned int j=0;j<rmc->num_newcoins;j++) for (unsigned int j=0;j<rmc->num_newcoins;j++)
if (NULL != rmc->denom_pubs[j].rsa_public_key) if (NULL != rmc->denom_pubs[j].rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (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); GNUNET_free (rmc->denom_pubs);
rmc->denom_pubs = NULL;
} }
if (NULL != rmc->hash_context) if (NULL != rmc->hash_context)
{
GNUNET_CRYPTO_hash_context_abort (rmc->hash_context); GNUNET_CRYPTO_hash_context_abort (rmc->hash_context);
rmc->hash_context = NULL;
}
if (NULL != rmc->key_state)
{
TEH_KS_release (rmc->key_state);
rmc->key_state = NULL;
}
} }
/** /**
* Handle a "/refresh/melt" request after the first parsing has happened. * Handle a "/refresh/melt" request after the first parsing has
* We now need to validate the coins being melted and the session signature * happened. We now need to validate the coins being melted and the
* and then hand things of to execute the melt operation. This function * session signature and then hand things of to execute the melt
* parses the JSON arrays and then passes processing on to * operation. This function parses the JSON arrays and then passes
* #handle_refresh_melt_binary(). * processing on to #handle_refresh_melt_binary().
* *
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param new_denoms array of denomination keys * @param new_denoms array of denomination keys
@ -741,7 +646,6 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
/* 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). */
rmc.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[] = {
@ -765,7 +669,6 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
sizeof (struct TALER_TransferPublicKeyP)); sizeof (struct TALER_TransferPublicKeyP));
} }
rmc.num_newcoins = json_array_size (new_denoms); rmc.num_newcoins = json_array_size (new_denoms);
rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins, rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins,
struct TALER_DenominationPublicKey); struct TALER_DenominationPublicKey);
@ -798,8 +701,9 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
GNUNET_free (buf); GNUNET_free (buf);
} }
/* decode JSON data on coin to melt and check that this is a
valid coin */
{ {
/* decode JSON data on coin to melt */
struct TALER_AmountNBO melt_amount; struct TALER_AmountNBO melt_amount;
res = get_coin_public_info (connection, res = get_coin_public_info (connection,
@ -861,16 +765,58 @@ handle_refresh_melt_json (struct MHD_Connection *connection,
GNUNET_CRYPTO_hash_context_finish (rmc.hash_context, GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
&rmc.session_hash); &rmc.session_hash);
rmc.hash_context = NULL; rmc.hash_context = NULL;
/* verify signature on coins to melt */
res = verify_coin_public_info (connection, rmc.key_state = TEH_KS_acquire ();
&rmc.session_hash, rmc.dki = TEH_KS_denomination_key_lookup (rmc.key_state,
&rmc.coin_melt_details); &rmc.coin_melt_details.coin_info.denom_pub,
if (GNUNET_OK != res) TEH_KS_DKU_DEPOSIT);
if (NULL == rmc.dki)
{
TEH_KS_release (rmc.key_state);
TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
return TEH_RESPONSE_reply_arg_unknown (connection,
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
"denom_pub");
}
/* verify signature of coin for melt operation */
{
struct TALER_RefreshMeltCoinAffirmationPS body;
struct TALER_Amount fee_refresh;
TALER_amount_ntoh (&fee_refresh,
&rmc.dki->issue.properties.fee_refresh);
rmc.coin_melt_details.melt_fee = fee_refresh;
body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
body.session_hash = rmc.session_hash;
TALER_amount_hton (&body.amount_with_fee,
&rmc.coin_melt_details.melt_amount_with_fee);
TALER_amount_hton (&body.melt_fee,
&fee_refresh);
body.coin_pub = rmc.coin_melt_details.coin_info.coin_pub;
if (TALER_amount_cmp (&fee_refresh,
&rmc.coin_melt_details.melt_amount_with_fee) > 0)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
cleanup_rmc (&rmc); cleanup_rmc (&rmc);
return mhd_ret; return TEH_RESPONSE_reply_external_error (connection,
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
"melt amount smaller than melting fee");
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&body.purpose,
&rmc.coin_melt_details.melt_sig.eddsa_signature,
&rmc.coin_melt_details.coin_info.coin_pub.eddsa_pub))
{
GNUNET_break_op (0);
cleanup_rmc (&rmc);
return TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
"confirm_sig");
}
} }
/* prepare commit */ /* prepare commit */

View File

@ -1553,10 +1553,11 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init (); refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init ();
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
hash); hash);
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
state->plugin->create_refresh_session (state->session, state->plugin->create_refresh_session (state->session,
state->session, state->session,
hash, hash,
refresh_session); refresh_session));
state->cmd[state->i].exposed.data.session_hash = hash; state->cmd[state->i].exposed.data.session_hash = hash;
PERF_TALER_EXCHANGEDB_refresh_session_free (refresh_session); PERF_TALER_EXCHANGEDB_refresh_session_free (refresh_session);
GNUNET_free (refresh_session); GNUNET_free (refresh_session);
@ -1589,11 +1590,12 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
denom_index = state->cmd[state->i].details.insert_refresh_order.index_denom; denom_index = state->cmd[state->i].details.insert_refresh_order.index_denom;
session_hash = state->cmd[hash_index].exposed.data.session_hash; session_hash = state->cmd[hash_index].exposed.data.session_hash;
denom = state->cmd[denom_index].exposed.data.dki; denom = state->cmd[denom_index].exposed.data.dki;
GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
state->plugin->insert_refresh_order (state->plugin->cls, state->plugin->insert_refresh_order (state->plugin->cls,
state->session, state->session,
session_hash, session_hash,
1, 1,
&denom->denom_pub); &denom->denom_pub));
} }
break; break;
@ -1616,18 +1618,18 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN: case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN:
{ {
int ret; enum GNUNET_DB_QueryStatus qs;
unsigned int hash_index; unsigned int hash_index;
struct TALER_EXCHANGEDB_RefreshCommitCoin *refresh_commit; struct TALER_EXCHANGEDB_RefreshCommitCoin *refresh_commit;
hash_index = state->cmd[state->i].details.insert_refresh_commit_coin.index_hash; hash_index = state->cmd[state->i].details.insert_refresh_commit_coin.index_hash;
refresh_commit = PERF_TALER_EXCHANGEDB_refresh_commit_coin_init (); refresh_commit = PERF_TALER_EXCHANGEDB_refresh_commit_coin_init ();
ret = state->plugin->insert_refresh_commit_coins (state->plugin->cls, qs = state->plugin->insert_refresh_commit_coins (state->plugin->cls,
state->session, state->session,
state->cmd[hash_index].exposed.data.session_hash, state->cmd[hash_index].exposed.data.session_hash,
1, 1,
refresh_commit); refresh_commit);
GNUNET_assert (GNUNET_OK == ret); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
} }
break; break;

View File

@ -3344,11 +3344,9 @@ postgres_get_refresh_session (void *cls,
* @param session database handle to use * @param session database handle to use
* @param session_hash hash over the melt to use to locate the session * @param session_hash hash over the melt to use to locate the session
* @param refresh_session session data to store * @param refresh_session session data to store
* @return #GNUNET_YES on success, * @return query status for the transaction
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on DB failure
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_create_refresh_session (void *cls, postgres_create_refresh_session (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -3366,7 +3364,7 @@ postgres_create_refresh_session (void *cls,
int ret; int ret;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
/* check if the coin is already known */ /* check if the coin is already known (FIXME: #5010) */
ret = get_known_coin (cls, ret = get_known_coin (cls,
session, session,
&refresh_session->melt.coin.coin_pub, &refresh_session->melt.coin.coin_pub,
@ -3384,11 +3382,11 @@ postgres_create_refresh_session (void *cls,
if (0 > qs) if (0 > qs)
{ {
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return GNUNET_SYSERR; return qs;
} }
} }
return execute_prepared_non_select (session, return GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_refresh_session", "insert_refresh_session",
params); params);
} }
@ -3403,11 +3401,9 @@ postgres_create_refresh_session (void *cls,
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param num_newcoins number of coins to generate, size of the @a denom_pubs array * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
* @param denom_pubs array denominations of the coins to create * @param denom_pubs array denominations of the coins to create
* @return #GNUNET_OK on success * @return query status for the transaction
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on internal error
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_order (void *cls, postgres_insert_refresh_order (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -3426,18 +3422,18 @@ postgres_insert_refresh_order (void *cls,
GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash), GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
int ret; enum GNUNET_DB_QueryStatus qs;
GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key, GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key,
&denom_pub_hash); &denom_pub_hash);
ret = execute_prepared_non_select (session, qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_refresh_order", "insert_refresh_order",
params); params);
if (GNUNET_OK != ret) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
return ret; return qs;
} }
} }
return GNUNET_OK; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }
@ -3524,11 +3520,9 @@ postgres_get_refresh_order (void *cls,
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param num_newcoins coin index size of the @a commit_coins array * @param num_newcoins coin index size of the @a commit_coins array
* @param commit_coins array of coin commitments to store * @param commit_coins array of coin commitments to store
* @return #GNUNET_OK on success * @return query transaction status
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on error
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_commit_coins (void *cls, postgres_insert_refresh_commit_coins (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -3544,15 +3538,15 @@ postgres_insert_refresh_commit_coins (void *cls,
commit_coins[coin_off].coin_ev_size), commit_coins[coin_off].coin_ev_size),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
int ret; enum GNUNET_DB_QueryStatus qs;
ret = execute_prepared_non_select (session, qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_refresh_commit_coin", "insert_refresh_commit_coin",
params); params);
if (GNUNET_OK != ret) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
return ret; return qs;
} }
return GNUNET_OK; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }
@ -3640,11 +3634,9 @@ postgres_get_refresh_commit_coins (void *cls,
* @param session database connection to use * @param session database connection to use
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param tp transfer public key to store * @param tp transfer public key to store
* @return #GNUNET_SYSERR on internal error, * @return transaction status
* #GNUNET_NO on transient errors
* #GNUNET_OK on success
*/ */
static int static enum GNUNET_DB_QueryStatus
postgres_insert_refresh_transfer_public_key (void *cls, postgres_insert_refresh_transfer_public_key (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -3656,7 +3648,7 @@ postgres_insert_refresh_transfer_public_key (void *cls,
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
return execute_prepared_non_select (session, return GNUNET_PQ_eval_prepared_non_select (session->conn,
"insert_transfer_public_key", "insert_transfer_public_key",
params); params);
} }

View File

@ -349,7 +349,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session *session,
ccoin->coin_ev, ccoin->coin_ev,
ccoin->coin_ev_size); ccoin->coin_ev_size);
} }
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_commit_coins (plugin->cls, plugin->insert_refresh_commit_coins (plugin->cls,
session, session,
session_hash, session_hash,
@ -417,7 +417,7 @@ test_refresh_commit_links (struct TALER_EXCHANGEDB_Session *session,
&tp)); &tp));
for (i=0;i<TALER_CNC_KAPPA;i++) for (i=0;i<TALER_CNC_KAPPA;i++)
RND_BLK (&rctp[i]); RND_BLK (&rctp[i]);
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refresh_transfer_public_key (plugin->cls, plugin->insert_refresh_transfer_public_key (plugin->cls,
session, session,
session_hash, session_hash,
@ -572,7 +572,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
meltp->melt_fee = fee_refresh; meltp->melt_fee = fee_refresh;
} }
FAILIF (GNUNET_OK != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->create_refresh_session (plugin->cls, plugin->create_refresh_session (plugin->cls,
session, session,
&session_hash, &session_hash,

View File

@ -1457,11 +1457,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database handle to use * @param session database handle to use
* @param session_hash hash over the melt to use to locate the session * @param session_hash hash over the melt to use to locate the session
* @param refresh_session session data to store * @param refresh_session session data to store
* @return #GNUNET_YES on success, * @return query status for the transaction
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on DB failure
*/ */
int enum GNUNET_DB_QueryStatus
(*create_refresh_session) (void *cls, (*create_refresh_session) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -1477,11 +1475,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param num_newcoins number of coins to generate, size of the @a denom_pubs array * @param num_newcoins number of coins to generate, size of the @a denom_pubs array
* @param denom_pubs array denominations of the coins to create * @param denom_pubs array denominations of the coins to create
* @return #GNUNET_OK on success * @return query status for the transaction
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on internal error
*/ */
int enum GNUNET_DB_QueryStatus
(*insert_refresh_order) (void *cls, (*insert_refresh_order) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -1517,11 +1513,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param num_newcoins coin index size of the @a commit_coins array * @param num_newcoins coin index size of the @a commit_coins array
* @param commit_coin array of coin commitments to store * @param commit_coin array of coin commitments to store
* @return #GNUNET_OK on success * @return query status for the transaction
* #GNUNET_NO on transient error
* #GNUNET_SYSERR on error
*/ */
int enum GNUNET_DB_QueryStatus
(*insert_refresh_commit_coins) (void *cls, (*insert_refresh_commit_coins) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,
@ -1568,11 +1562,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection to use * @param session database connection to use
* @param session_hash hash to identify refresh session * @param session_hash hash to identify refresh session
* @param tp public key to store * @param tp public key to store
* @return #GNUNET_SYSERR on internal error * @return query status for the transaction
* #GNUNET_NO on transient errors
* #GNUNET_OK on success
*/ */
int enum GNUNET_DB_QueryStatus
(*insert_refresh_transfer_public_key) (void *cls, (*insert_refresh_transfer_public_key) (void *cls,
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct GNUNET_HashCode *session_hash, const struct GNUNET_HashCode *session_hash,