diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_melt.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_melt.c | 542 |
1 files changed, 203 insertions, 339 deletions
diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c index af0a0a636..ac3902e3f 100644 --- a/src/exchange/taler-exchange-httpd_melt.c +++ b/src/exchange/taler-exchange-httpd_melt.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -34,56 +34,6 @@ /** - * Send a response for a failed "melt" request. The - * transaction history of the given coin demonstrates that the - * @a residual value of the coin is below the @a requested - * contribution of the coin for the melt. Thus, the exchange - * refuses the melt operation. - * - * @param connection the connection to send the response to - * @param coin_pub public key of the coin - * @param coin_value original value of the coin - * @param tl transaction history for the coin - * @param requested how much this coin was supposed to contribute, including fee - * @param residual remaining value of the coin (after subtracting @a tl) - * @return a MHD result code - */ -static MHD_RESULT -reply_melt_insufficient_funds ( - struct MHD_Connection *connection, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - const struct TALER_Amount *coin_value, - struct TALER_EXCHANGEDB_TransactionList *tl, - const struct TALER_Amount *requested, - const struct TALER_Amount *residual) -{ - json_t *history; - - history = TEH_RESPONSE_compile_transaction_history (coin_pub, - tl); - if (NULL == history) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS, - NULL); - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_CONFLICT, - TALER_JSON_pack_ec (TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS), - GNUNET_JSON_pack_data_auto ("coin_pub", - coin_pub), - TALER_JSON_pack_amount ("original_value", - coin_value), - TALER_JSON_pack_amount ("residual_value", - residual), - TALER_JSON_pack_amount ("requested_value", - requested), - GNUNET_JSON_pack_array_steal ("history", - history)); -} - - -/** * Send a response to a "melt" request. * * @param connection the connection to send the response to @@ -98,19 +48,17 @@ reply_melt_success (struct MHD_Connection *connection, { struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; - struct TALER_RefreshMeltConfirmationPS body = { - .purpose.size = htonl (sizeof (body)), - .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT), - .rc = *rc, - .noreveal_index = htonl (noreveal_index) - }; enum TALER_ErrorCode ec; if (TALER_EC_NONE != - (ec = TEH_keys_exchange_sign (&body, - &pub, - &sig))) + (ec = TALER_exchange_online_melt_confirmation_sign ( + &TEH_keys_exchange_sign_, + rc, + noreveal_index, + &pub, + &sig))) { + GNUNET_break (0); return TALER_MHD_reply_with_ec (connection, ec, NULL); @@ -140,6 +88,11 @@ struct MeltContext struct TALER_EXCHANGEDB_Refresh refresh_session; /** + * UUID of the coin in the known_coins table. + */ + uint64_t known_coin_id; + + /** * Information about the @e coin's value. */ struct TALER_Amount coin_value; @@ -150,6 +103,11 @@ struct MeltContext struct TALER_Amount coin_refresh_fee; /** + * Refresh master secret, if any of the fresh denominations use CS. + */ + struct TALER_RefreshMasterSecretP rms; + + /** * Set to true if this coin's denomination was revoked and the operation * is thus only allowed for zombie coins where the transaction * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP. @@ -162,128 +120,14 @@ struct MeltContext */ bool coin_is_dirty; + /** + * True if @e rms is missing. + */ + bool no_rms; }; /** - * 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 cost of this melt transaction */ - spent = rmc->refresh_session.amount_with_fee; - - /* get historic transaction costs of this coin, including recoups as - we might be a zombie coin */ - qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, - &rmc->refresh_session.coin.coin_pub, - GNUNET_YES, - &tl); - if (0 > qs) - { - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "coin transaction history"); - return qs; - } - if (rmc->zombie_required) - { - /* The denomination key is only usable for a melt if this is a true - zombie coin, i.e. it was refreshed and the resulting fresh coin was - then recouped. Check that this is truly the case. */ - for (struct TALER_EXCHANGEDB_TransactionList *tp = tl; - NULL != tp; - tp = tp->next) - { - if (TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP == tp->type) - { - rmc->zombie_required = false; /* clear flag: was satisfied! */ - break; - } - } - if (rmc->zombie_required) - { - /* zombie status not satisfied */ - GNUNET_break_op (0); - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - if (GNUNET_OK != - TALER_EXCHANGEDB_calculate_transaction_list_totals (tl, - &spent, - &spent)) - { - GNUNET_break (0); - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_MELT_COIN_HISTORY_COMPUTATION_FAILED, - NULL); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - /* Refuse to refresh when the coin's value is insufficient - for the cost of all transactions. */ - if (0 > TALER_amount_cmp (&rmc->coin_value, - &spent)) - { - struct TALER_Amount coin_residual; - struct TALER_Amount spent_already; - - /* First subtract the melt cost from 'spent' to - compute the total amount already spent of the coin */ - GNUNET_assert (0 <= - TALER_amount_subtract (&spent_already, - &spent, - &rmc->refresh_session.amount_with_fee)); - /* The residual coin value is the original coin value minus - what we have spent (before the melt) */ - GNUNET_assert (0 <= - TALER_amount_subtract (&coin_residual, - &rmc->coin_value, - &spent_already)); - *mhd_ret = reply_melt_insufficient_funds ( - connection, - &rmc->refresh_session.coin.coin_pub, - &rmc->coin_value, - tl, - &rmc->refresh_session.amount_with_fee, - &coin_residual); - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - /* we're good, coin has sufficient funds to be melted */ - TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, - tl); - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** * Execute a "melt". We have been given a list of valid * coins and a request to melt them into the given @a * refresh_session_pub. Check that the coins all have the required @@ -309,57 +153,22 @@ melt_transaction (void *cls, { struct MeltContext *rmc = cls; enum GNUNET_DB_QueryStatus qs; - uint32_t noreveal_index; - - /* First, make sure coin is 'known' in database */ - if (! rmc->coin_is_dirty) - { - qs = TEH_make_coin_known (&rmc->refresh_session.coin, - connection, - mhd_ret); - if (qs < 0) - return qs; - } - - /* Check if we already created a matching refresh_session */ - qs = TEH_plugin->get_melt_index (TEH_plugin->cls, - &rmc->refresh_session.rc, - &noreveal_index); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - TALER_LOG_DEBUG ("Coin was previously melted, returning old reply\n"); - *mhd_ret = reply_melt_success (connection, - &rmc->refresh_session.rc, - noreveal_index); - /* 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; - } - 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, - "melt index"); - return qs; - } - - /* 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 */ + bool balance_ok; /* pick challenge and persist it */ rmc->refresh_session.noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, TALER_CNC_KAPPA); - if (0 >= - (qs = TEH_plugin->insert_melt (TEH_plugin->cls, - &rmc->refresh_session))) + + if (0 > + (qs = TEH_plugin->do_melt (TEH_plugin->cls, + rmc->no_rms + ? NULL + : &rmc->rms, + &rmc->refresh_session, + rmc->known_coin_id, + &rmc->zombie_required, + &balance_ok))) { if (GNUNET_DB_STATUS_SOFT_ERROR != qs) { @@ -371,62 +180,90 @@ melt_transaction (void *cls, } return qs; } + GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); + if (rmc->zombie_required) + { + GNUNET_break_op (0); + *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 (! balance_ok) + { + GNUNET_break_op (0); + *mhd_ret + = TEH_RESPONSE_reply_coin_insufficient_funds ( + connection, + TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, + &rmc->refresh_session.coin.denom_pub_hash, + &rmc->refresh_session.coin.coin_pub); + return GNUNET_DB_STATUS_HARD_ERROR; + } + /* All good, commit, final response will be generated by caller */ + TEH_METRICS_num_success[TEH_MT_SUCCESS_MELT]++; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } /** * Handle a "melt" request after the first parsing has - * happened. We now need to validate the coins being melted and the - * session signature and then hand things of to execute the melt - * operation. This function parses the JSON arrays and then passes - * processing on to #melt_transaction(). + * happened. Performs the database transactions. * * @param connection the MHD connection to handle * @param[in,out] rmc details about the melt request * @return MHD result code */ static MHD_RESULT -handle_melt (struct MHD_Connection *connection, - struct MeltContext *rmc) +database_melt (struct MHD_Connection *connection, + struct MeltContext *rmc) { - /* verify signature of coin for melt operation */ + if (GNUNET_SYSERR == + TEH_plugin->preflight (TEH_plugin->cls)) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_START_FAILED, + "preflight failure"); + } + + /* first, make sure coin is known */ + if (! rmc->coin_is_dirty) { - struct TALER_RefreshMeltCoinAffirmationPS body = { - .purpose.size = htonl (sizeof (body)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT), - .rc = rmc->refresh_session.rc, - .h_denom_pub = rmc->refresh_session.coin.denom_pub_hash, - .coin_pub = rmc->refresh_session.coin.coin_pub - }; - - TALER_amount_hton (&body.amount_with_fee, - &rmc->refresh_session.amount_with_fee); - TALER_amount_hton (&body.melt_fee, - &rmc->coin_refresh_fee); + MHD_RESULT mhd_ret = MHD_NO; + enum GNUNET_DB_QueryStatus qs; - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_WALLET_COIN_MELT, - &body, - &rmc->refresh_session.coin_sig.eddsa_signature, - &rmc->refresh_session.coin.coin_pub.eddsa_pub)) + for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++) { - GNUNET_break_op (0); + qs = TEH_make_coin_known (&rmc->refresh_session.coin, + connection, + &rmc->known_coin_id, + &mhd_ret); + if (GNUNET_DB_STATUS_SOFT_ERROR != qs) + break; + } + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + { + GNUNET_break (0); return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID, - NULL); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_COMMIT_FAILED, + "make_coin_known"); } + if (qs < 0) + return mhd_ret; } - /* run database transaction */ + /* run main database transaction */ { MHD_RESULT mhd_ret; if (GNUNET_OK != TEH_DB_run_transaction (connection, "run melt", + TEH_MT_REQUEST_MELT, &mhd_ret, &melt_transaction, rmc)) @@ -449,10 +286,10 @@ handle_melt (struct MHD_Connection *connection, * @return MHD status code */ static MHD_RESULT -check_for_denomination_key (struct MHD_Connection *connection, - struct MeltContext *rmc) +check_melt_valid (struct MHD_Connection *connection, + struct MeltContext *rmc) { - /* Baseline: check if deposits/refreshs are generally + /* Baseline: check if deposits/refreshes are generally simply still allowed for this denomination */ struct TEH_DenominationKey *dk; MHD_RESULT mret; @@ -463,38 +300,89 @@ check_for_denomination_key (struct MHD_Connection *connection, &mret); if (NULL == dk) return mret; - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal)) - { - struct GNUNET_TIME_Absolute now; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal.abs_time)) + { /* Way too late now, even zombies have expired */ return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, &rmc->refresh_session.coin.denom_pub_hash, - now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, "MELT"); } - if (GNUNET_TIME_absolute_is_future (dk->meta.start)) - { - struct GNUNET_TIME_Absolute now; - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); + if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) + { /* This denomination is not yet valid */ return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, &rmc->refresh_session.coin.denom_pub_hash, - now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, "MELT"); } - if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit)) + + rmc->coin_refresh_fee = dk->meta.fees.refresh; + rmc->coin_value = dk->meta.value; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Melted coin's denomination is worth %s\n", + TALER_amount2s (&dk->meta.value)); + + /* sanity-check that "total melt amount > melt fee" */ + if (0 < + TALER_amount_cmp (&rmc->coin_refresh_fee, + &rmc->refresh_session.amount_with_fee)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION, + NULL); + } + switch (dk->denom_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++; + break; + case GNUNET_CRYPTO_BSA_CS: + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++; + break; + default: + break; + } + if (GNUNET_OK != + TALER_test_coin_valid (&rmc->refresh_session.coin, + &dk->denom_pub)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, + NULL); + } + + /* verify signature of coin for melt operation */ + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + if (GNUNET_OK != + TALER_wallet_melt_verify (&rmc->refresh_session.amount_with_fee, + &rmc->coin_refresh_fee, + &rmc->refresh_session.rc, + &rmc->refresh_session.coin.denom_pub_hash, + &rmc->refresh_session.coin.h_age_commitment, + &rmc->refresh_session.coin.coin_pub, + &rmc->refresh_session.coin_sig)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID, + NULL); + } + + if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) { /* We are past deposit expiration time, but maybe this is a zombie? */ - struct GNUNET_HashCode denom_hash; + struct TALER_DenominationHashP denom_hash; enum GNUNET_DB_QueryStatus qs; /* Check that the coin is dirty (we have seen it before), as we will @@ -503,6 +391,7 @@ check_for_denomination_key (struct MHD_Connection *connection, qs = TEH_plugin->get_coin_denomination ( TEH_plugin->cls, &rmc->refresh_session.coin.coin_pub, + &rmc->known_coin_id, &denom_hash); if (0 > qs) { @@ -513,87 +402,52 @@ check_for_denomination_key (struct MHD_Connection *connection, TALER_EC_GENERIC_DB_FETCH_FAILED, "coin denomination"); } - /* sanity check */ - GNUNET_break (0 == - GNUNET_memcmp (&denom_hash, - &rmc->refresh_session.coin.denom_pub_hash)); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { - struct GNUNET_TIME_Absolute now; - - now = GNUNET_TIME_absolute_get (); - (void) GNUNET_TIME_round_abs (&now); /* We never saw this coin before, so _this_ justification is not OK */ return TEH_RESPONSE_reply_expired_denom_pub_hash ( connection, &rmc->refresh_session.coin.denom_pub_hash, - now, TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, "MELT"); } - else + /* Minor optimization: no need to run the + "ensure_coin_known" part of the transaction */ + rmc->coin_is_dirty = true; + /* sanity check */ + if (0 != + GNUNET_memcmp (&denom_hash, + &rmc->refresh_session.coin.denom_pub_hash)) { - /* Minor optimization: no need to run the - "ensure_coin_known" part of the transaction */ - rmc->coin_is_dirty = true; + GNUNET_break_op (0); + return TALER_MHD_reply_with_ec ( + connection, + TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY, + TALER_B2S (&denom_hash)); } rmc->zombie_required = true; /* check later that zombie is satisfied */ } - rmc->coin_refresh_fee = dk->meta.fee_refresh; - rmc->coin_value = dk->meta.value; - /* check coin is actually properly signed */ - if (GNUNET_OK != - TALER_test_coin_valid (&rmc->refresh_session.coin, - &dk->denom_pub)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, - NULL); - } - - /* sanity-check that "total melt amount > melt fee" */ - if (0 < - TALER_amount_cmp (&rmc->coin_refresh_fee, - &rmc->refresh_session.amount_with_fee)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION, - NULL); - } - return handle_melt (connection, - rmc); + return database_melt (connection, + rmc); } -/** - * Handle a "/coins/$COIN_PUB/melt" request. Parses the request into the JSON - * components and then hands things of to #check_for_denomination_key() to - * validate the melted coins, the signature and execute the melt using - * handle_melt(). - - * @param connection the MHD connection to handle - * @param coin_pub public key of the coin - * @param root uploaded JSON data - * @return MHD result code - */ MHD_RESULT TEH_handler_melt (struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, const json_t *root) { struct MeltContext rmc; - enum GNUNET_GenericReturnValue ret; - MHD_RESULT res; struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_denomination_signature ("denom_sig", - &rmc.refresh_session.coin.denom_sig), + TALER_JSON_spec_denom_sig ("denom_sig", + &rmc.refresh_session.coin.denom_sig), GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", &rmc.refresh_session.coin.denom_pub_hash), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("age_commitment_hash", + &rmc.refresh_session.coin.h_age_commitment), + &rmc.refresh_session.coin.no_age_commitment), GNUNET_JSON_spec_fixed_auto ("confirm_sig", &rmc.refresh_session.coin_sig), TALER_JSON_spec_amount ("value_with_fee", @@ -601,23 +455,33 @@ TEH_handler_melt (struct MHD_Connection *connection, &rmc.refresh_session.amount_with_fee), GNUNET_JSON_spec_fixed_auto ("rc", &rmc.refresh_session.rc), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_fixed_auto ("rms", + &rmc.rms), + &rmc.no_rms), GNUNET_JSON_spec_end () }; - memset (&rmc, - 0, - sizeof (rmc)); + memset (&rmc, 0, sizeof (rmc)); rmc.refresh_session.coin.coin_pub = *coin_pub; - ret = TALER_MHD_parse_json_data (connection, - root, - spec); - if (GNUNET_OK != ret) - return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; - - res = check_for_denomination_key (connection, - &rmc); - GNUNET_JSON_parse_free (spec); - return res; + + { + enum GNUNET_GenericReturnValue ret; + ret = TALER_MHD_parse_json_data (connection, + root, + spec); + if (GNUNET_OK != ret) + return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; + } + + { + MHD_RESULT res; + + res = check_melt_valid (connection, + &rmc); + GNUNET_JSON_parse_free (spec); + return res; + } } |