LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_melt.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 98 154 63.6 %
Date: 2021-08-30 06:43:37 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2020 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_melt.c
      18             :  * @brief Handle melt requests
      19             :  * @author Florian Dold
      20             :  * @author Benedikt Mueller
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <jansson.h>
      26             : #include <microhttpd.h>
      27             : #include "taler_json_lib.h"
      28             : #include "taler_mhd_lib.h"
      29             : #include "taler-exchange-httpd_mhd.h"
      30             : #include "taler-exchange-httpd_melt.h"
      31             : #include "taler-exchange-httpd_responses.h"
      32             : #include "taler-exchange-httpd_keys.h"
      33             : #include "taler_exchangedb_lib.h"
      34             : 
      35             : 
      36             : /**
      37             :  * Send a response for a failed "melt" request.  The
      38             :  * transaction history of the given coin demonstrates that the
      39             :  * @a residual value of the coin is below the @a requested
      40             :  * contribution of the coin for the melt.  Thus, the exchange
      41             :  * refuses the melt operation.
      42             :  *
      43             :  * @param connection the connection to send the response to
      44             :  * @param coin_pub public key of the coin
      45             :  * @param coin_value original value of the coin
      46             :  * @param tl transaction history for the coin
      47             :  * @param requested how much this coin was supposed to contribute, including fee
      48             :  * @param residual remaining value of the coin (after subtracting @a tl)
      49             :  * @return a MHD result code
      50             :  */
      51             : static MHD_RESULT
      52           3 : reply_melt_insufficient_funds (
      53             :   struct MHD_Connection *connection,
      54             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
      55             :   const struct TALER_Amount *coin_value,
      56             :   struct TALER_EXCHANGEDB_TransactionList *tl,
      57             :   const struct TALER_Amount *requested,
      58             :   const struct TALER_Amount *residual)
      59             : {
      60             :   json_t *history;
      61             : 
      62           3 :   history = TEH_RESPONSE_compile_transaction_history (coin_pub,
      63             :                                                       tl);
      64           3 :   if (NULL == history)
      65           0 :     return TALER_MHD_reply_with_error (connection,
      66             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
      67             :                                        TALER_EC_EXCHANGE_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
      68             :                                        NULL);
      69           3 :   return TALER_MHD_REPLY_JSON_PACK (
      70             :     connection,
      71             :     MHD_HTTP_CONFLICT,
      72             :     TALER_JSON_pack_ec (TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS),
      73             :     GNUNET_JSON_pack_data_auto ("coin_pub",
      74             :                                 coin_pub),
      75             :     TALER_JSON_pack_amount ("original_value",
      76             :                             coin_value),
      77             :     TALER_JSON_pack_amount ("residual_value",
      78             :                             residual),
      79             :     TALER_JSON_pack_amount ("requested_value",
      80             :                             requested),
      81             :     GNUNET_JSON_pack_array_steal ("history",
      82             :                                   history));
      83             : }
      84             : 
      85             : 
      86             : /**
      87             :  * Send a response to a "melt" request.
      88             :  *
      89             :  * @param connection the connection to send the response to
      90             :  * @param rc value the client committed to
      91             :  * @param noreveal_index which index will the client not have to reveal
      92             :  * @return a MHD status code
      93             :  */
      94             : static MHD_RESULT
      95          17 : reply_melt_success (struct MHD_Connection *connection,
      96             :                     const struct TALER_RefreshCommitmentP *rc,
      97             :                     uint32_t noreveal_index)
      98             : {
      99             :   struct TALER_ExchangePublicKeyP pub;
     100             :   struct TALER_ExchangeSignatureP sig;
     101          17 :   struct TALER_RefreshMeltConfirmationPS body = {
     102          17 :     .purpose.size = htonl (sizeof (body)),
     103          17 :     .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT),
     104             :     .rc = *rc,
     105          17 :     .noreveal_index = htonl (noreveal_index)
     106             :   };
     107             :   enum TALER_ErrorCode ec;
     108             : 
     109          17 :   if (TALER_EC_NONE !=
     110          17 :       (ec = TEH_keys_exchange_sign (&body,
     111             :                                     &pub,
     112             :                                     &sig)))
     113             :   {
     114           0 :     return TALER_MHD_reply_with_ec (connection,
     115             :                                     ec,
     116             :                                     NULL);
     117             :   }
     118          17 :   return TALER_MHD_REPLY_JSON_PACK (
     119             :     connection,
     120             :     MHD_HTTP_OK,
     121             :     GNUNET_JSON_pack_uint64 ("noreveal_index",
     122             :                              noreveal_index),
     123             :     GNUNET_JSON_pack_data_auto ("exchange_sig",
     124             :                                 &sig),
     125             :     GNUNET_JSON_pack_data_auto ("exchange_pub",
     126             :                                 &pub));
     127             : }
     128             : 
     129             : 
     130             : /**
     131             :  * Context for the melt operation.
     132             :  */
     133             : struct MeltContext
     134             : {
     135             : 
     136             :   /**
     137             :    * noreveal_index is only initialized during
     138             :    * #melt_transaction().
     139             :    */
     140             :   struct TALER_EXCHANGEDB_Refresh refresh_session;
     141             : 
     142             :   /**
     143             :    * Information about the @e coin's value.
     144             :    */
     145             :   struct TALER_Amount coin_value;
     146             : 
     147             :   /**
     148             :    * Information about the @e coin's refresh fee.
     149             :    */
     150             :   struct TALER_Amount coin_refresh_fee;
     151             : 
     152             :   /**
     153             :    * Set to true if this coin's denomination was revoked and the operation
     154             :    * is thus only allowed for zombie coins where the transaction
     155             :    * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP.
     156             :    */
     157             :   bool zombie_required;
     158             : 
     159             :   /**
     160             :    * We already checked and noticed that the coin is known. Hence we
     161             :    * can skip the "ensure_coin_known" step of the transaction.
     162             :    */
     163             :   bool coin_is_dirty;
     164             : 
     165             : };
     166             : 
     167             : 
     168             : /**
     169             :  * Check that the coin has sufficient funds left for the selected
     170             :  * melt operation.
     171             :  *
     172             :  * @param connection the connection to send errors to
     173             :  * @param[in,out] rmc melt context
     174             :  * @param[out] mhd_ret status code to return to MHD on hard error
     175             :  * @return transaction status code
     176             :  */
     177             : static enum GNUNET_DB_QueryStatus
     178          18 : refresh_check_melt (struct MHD_Connection *connection,
     179             :                     struct MeltContext *rmc,
     180             :                     MHD_RESULT *mhd_ret)
     181             : {
     182             :   struct TALER_EXCHANGEDB_TransactionList *tl;
     183             :   struct TALER_Amount spent;
     184             :   enum GNUNET_DB_QueryStatus qs;
     185             : 
     186             :   /* Start with cost of this melt transaction */
     187          18 :   spent = rmc->refresh_session.amount_with_fee;
     188             : 
     189             :   /* get historic transaction costs of this coin, including recoups as
     190             :      we might be a zombie coin */
     191          18 :   qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
     192          18 :                                           &rmc->refresh_session.coin.coin_pub,
     193             :                                           GNUNET_YES,
     194             :                                           &tl);
     195          18 :   if (0 > qs)
     196             :   {
     197           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     198           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     199             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     200             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     201             :                                              "coin transaction history");
     202           0 :     return qs;
     203             :   }
     204          18 :   if (rmc->zombie_required)
     205             :   {
     206             :     /* The denomination key is only usable for a melt if this is a true
     207             :        zombie coin, i.e. it was refreshed and the resulting fresh coin was
     208             :        then recouped. Check that this is truly the case. */
     209           0 :     for (struct TALER_EXCHANGEDB_TransactionList *tp = tl;
     210             :          NULL != tp;
     211           0 :          tp = tp->next)
     212             :     {
     213           0 :       if (TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP == tp->type)
     214             :       {
     215           0 :         rmc->zombie_required = false; /* clear flag: was satisfied! */
     216           0 :         break;
     217             :       }
     218             :     }
     219           0 :     if (rmc->zombie_required)
     220             :     {
     221             :       /* zombie status not satisfied */
     222           0 :       GNUNET_break_op (0);
     223           0 :       TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     224             :                                               tl);
     225           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     226             :                                              MHD_HTTP_BAD_REQUEST,
     227             :                                              TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
     228             :                                              NULL);
     229           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     230             :     }
     231             :   }
     232          18 :   if (GNUNET_OK !=
     233          18 :       TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
     234             :                                                           &spent,
     235             :                                                           &spent))
     236             :   {
     237           0 :     GNUNET_break (0);
     238           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     239             :                                             tl);
     240           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     241             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     242             :                                            TALER_EC_EXCHANGE_MELT_COIN_HISTORY_COMPUTATION_FAILED,
     243             :                                            NULL);
     244           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     245             :   }
     246             : 
     247             :   /* Refuse to refresh when the coin's value is insufficient
     248             :      for the cost of all transactions. */
     249          18 :   if (0 > TALER_amount_cmp (&rmc->coin_value,
     250             :                             &spent))
     251             :   {
     252             :     struct TALER_Amount coin_residual;
     253             :     struct TALER_Amount spent_already;
     254             : 
     255             :     /* First subtract the melt cost from 'spent' to
     256             :        compute the total amount already spent of the coin */
     257           3 :     GNUNET_assert (0 <=
     258             :                    TALER_amount_subtract (&spent_already,
     259             :                                           &spent,
     260             :                                           &rmc->refresh_session.amount_with_fee));
     261             :     /* The residual coin value is the original coin value minus
     262             :        what we have spent (before the melt) */
     263           3 :     GNUNET_assert (0 <=
     264             :                    TALER_amount_subtract (&coin_residual,
     265             :                                           &rmc->coin_value,
     266             :                                           &spent_already));
     267           6 :     *mhd_ret = reply_melt_insufficient_funds (
     268             :       connection,
     269           3 :       &rmc->refresh_session.coin.coin_pub,
     270           3 :       &rmc->coin_value,
     271             :       tl,
     272           3 :       &rmc->refresh_session.amount_with_fee,
     273             :       &coin_residual);
     274           3 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     275             :                                             tl);
     276           3 :     return GNUNET_DB_STATUS_HARD_ERROR;
     277             :   }
     278             : 
     279             :   /* we're good, coin has sufficient funds to be melted */
     280          15 :   TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     281             :                                           tl);
     282          15 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     283             : }
     284             : 
     285             : 
     286             : /**
     287             :  * Execute a "melt".  We have been given a list of valid
     288             :  * coins and a request to melt them into the given @a
     289             :  * refresh_session_pub.  Check that the coins all have the required
     290             :  * value left and if so, store that they have been melted and confirm
     291             :  * the melting operation to the client.
     292             :  *
     293             :  * If it returns a non-error code, the transaction logic MUST NOT
     294             :  * queue a MHD response.  IF it returns an hard error, the transaction
     295             :  * logic MUST queue a MHD response and set @a mhd_ret.  If it returns
     296             :  * the soft error code, the function MAY be called again to retry and
     297             :  * MUST not queue a MHD response.
     298             :  *
     299             :  * @param cls our `struct MeltContext`
     300             :  * @param connection MHD request which triggered the transaction
     301             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     302             :  *             if transaction failed (!)
     303             :  * @return transaction status
     304             :  */
     305             : static enum GNUNET_DB_QueryStatus
     306          21 : melt_transaction (void *cls,
     307             :                   struct MHD_Connection *connection,
     308             :                   MHD_RESULT *mhd_ret)
     309             : {
     310          21 :   struct MeltContext *rmc = cls;
     311             :   enum GNUNET_DB_QueryStatus qs;
     312             :   uint32_t noreveal_index;
     313             : 
     314             :   /* First, make sure coin is 'known' in database */
     315          21 :   if (! rmc->coin_is_dirty)
     316             :   {
     317          21 :     qs = TEH_make_coin_known (&rmc->refresh_session.coin,
     318             :                               connection,
     319             :                               mhd_ret);
     320          21 :     if (qs < 0)
     321           1 :       return qs;
     322             :   }
     323             : 
     324             :   /* Check if we already created a matching refresh_session */
     325          20 :   qs = TEH_plugin->get_melt_index (TEH_plugin->cls,
     326          20 :                                    &rmc->refresh_session.rc,
     327             :                                    &noreveal_index);
     328          20 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     329             :   {
     330           2 :     TALER_LOG_DEBUG ("Coin was previously melted, returning old reply\n");
     331           4 :     *mhd_ret = reply_melt_success (connection,
     332           2 :                                    &rmc->refresh_session.rc,
     333             :                                    noreveal_index);
     334             :     /* Note: we return "hard error" to ensure the wrapper
     335             :        does not retry the transaction, and to also not generate
     336             :        a "fresh" response (as we would on "success") */
     337           2 :     return GNUNET_DB_STATUS_HARD_ERROR;
     338             :   }
     339          18 :   if (0 > qs)
     340             :   {
     341           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     342           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     343             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     344             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     345             :                                              "melt index");
     346           0 :     return qs;
     347             :   }
     348             : 
     349             :   /* check coin has enough funds remaining on it to cover melt cost */
     350          18 :   qs = refresh_check_melt (connection,
     351             :                            rmc,
     352             :                            mhd_ret);
     353          18 :   if (0 > qs)
     354           3 :     return qs; /* if we failed, tell caller */
     355             : 
     356             :   /* pick challenge and persist it */
     357             :   rmc->refresh_session.noreveal_index
     358          15 :     = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
     359             :                                 TALER_CNC_KAPPA);
     360          15 :   if (0 >=
     361          15 :       (qs = TEH_plugin->insert_melt (TEH_plugin->cls,
     362          15 :                                      &rmc->refresh_session)))
     363             :   {
     364           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     365             :     {
     366           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     367             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     368             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
     369             :                                              "melt");
     370           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     371             :     }
     372           0 :     return qs;
     373             :   }
     374          15 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     375             : }
     376             : 
     377             : 
     378             : /**
     379             :  * Handle a "melt" request after the first parsing has
     380             :  * happened.  We now need to validate the coins being melted and the
     381             :  * session signature and then hand things of to execute the melt
     382             :  * operation.  This function parses the JSON arrays and then passes
     383             :  * processing on to #melt_transaction().
     384             :  *
     385             :  * @param connection the MHD connection to handle
     386             :  * @param[in,out] rmc details about the melt request
     387             :  * @return MHD result code
     388             :  */
     389             : static MHD_RESULT
     390          21 : handle_melt (struct MHD_Connection *connection,
     391             :              struct MeltContext *rmc)
     392             : {
     393             :   /* verify signature of coin for melt operation */
     394             :   {
     395          21 :     struct TALER_RefreshMeltCoinAffirmationPS body = {
     396          21 :       .purpose.size = htonl (sizeof (body)),
     397          21 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT),
     398             :       .rc = rmc->refresh_session.rc,
     399             :       .h_denom_pub = rmc->refresh_session.coin.denom_pub_hash,
     400             :       .coin_pub = rmc->refresh_session.coin.coin_pub
     401             :     };
     402             : 
     403          21 :     TALER_amount_hton (&body.amount_with_fee,
     404          21 :                        &rmc->refresh_session.amount_with_fee);
     405          21 :     TALER_amount_hton (&body.melt_fee,
     406          21 :                        &rmc->coin_refresh_fee);
     407             : 
     408          21 :     if (GNUNET_OK !=
     409          21 :         GNUNET_CRYPTO_eddsa_verify (
     410             :           TALER_SIGNATURE_WALLET_COIN_MELT,
     411             :           &body,
     412             :           &rmc->refresh_session.coin_sig.eddsa_signature,
     413             :           &rmc->refresh_session.coin.coin_pub.eddsa_pub))
     414             :     {
     415           0 :       GNUNET_break_op (0);
     416           0 :       return TALER_MHD_reply_with_error (connection,
     417             :                                          MHD_HTTP_FORBIDDEN,
     418             :                                          TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID,
     419             :                                          NULL);
     420             :     }
     421             :   }
     422             : 
     423             :   /* run database transaction */
     424             :   {
     425             :     MHD_RESULT mhd_ret;
     426             : 
     427          21 :     if (GNUNET_OK !=
     428          21 :         TEH_DB_run_transaction (connection,
     429             :                                 "run melt",
     430             :                                 &mhd_ret,
     431             :                                 &melt_transaction,
     432             :                                 rmc))
     433           6 :       return mhd_ret;
     434             :   }
     435             : 
     436             :   /* Success. Generate ordinary response. */
     437          15 :   return reply_melt_success (connection,
     438          15 :                              &rmc->refresh_session.rc,
     439             :                              rmc->refresh_session.noreveal_index);
     440             : }
     441             : 
     442             : 
     443             : /**
     444             :  * Check for information about the melted coin's denomination,
     445             :  * extracting its validity status and fee structure.
     446             :  *
     447             :  * @param connection HTTP connection we are handling
     448             :  * @param rmc parsed request information
     449             :  * @return MHD status code
     450             :  */
     451             : static MHD_RESULT
     452          21 : check_for_denomination_key (struct MHD_Connection *connection,
     453             :                             struct MeltContext *rmc)
     454             : {
     455             :   /* Baseline: check if deposits/refreshs are generally
     456             :      simply still allowed for this denomination */
     457             :   struct TEH_DenominationKey *dk;
     458             :   MHD_RESULT mret;
     459             : 
     460          21 :   dk = TEH_keys_denomination_by_hash (
     461          21 :     &rmc->refresh_session.coin.denom_pub_hash,
     462             :     connection,
     463             :     &mret);
     464          21 :   if (NULL == dk)
     465           0 :     return mret;
     466          21 :   if (GNUNET_TIME_absolute_is_past (dk->meta.expire_legal))
     467             :   {
     468             :     struct GNUNET_TIME_Absolute now;
     469             : 
     470           0 :     now = GNUNET_TIME_absolute_get ();
     471           0 :     (void) GNUNET_TIME_round_abs (&now);
     472             :     /* Way too late now, even zombies have expired */
     473           0 :     return TEH_RESPONSE_reply_expired_denom_pub_hash (
     474             :       connection,
     475           0 :       &rmc->refresh_session.coin.denom_pub_hash,
     476             :       now,
     477             :       TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     478             :       "MELT");
     479             :   }
     480          21 :   if (GNUNET_TIME_absolute_is_future (dk->meta.start))
     481             :   {
     482             :     struct GNUNET_TIME_Absolute now;
     483             : 
     484           0 :     now = GNUNET_TIME_absolute_get ();
     485           0 :     (void) GNUNET_TIME_round_abs (&now);
     486             :     /* This denomination is not yet valid */
     487           0 :     return TEH_RESPONSE_reply_expired_denom_pub_hash (
     488             :       connection,
     489           0 :       &rmc->refresh_session.coin.denom_pub_hash,
     490             :       now,
     491             :       TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     492             :       "MELT");
     493             :   }
     494          21 :   if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit))
     495             :   {
     496             :     /* We are past deposit expiration time, but maybe this is a zombie? */
     497             :     struct GNUNET_HashCode denom_hash;
     498             :     enum GNUNET_DB_QueryStatus qs;
     499             : 
     500             :     /* Check that the coin is dirty (we have seen it before), as we will
     501             :        not just allow melting of a *fresh* coin where the denomination was
     502             :        revoked (those must be recouped) */
     503           0 :     qs = TEH_plugin->get_coin_denomination (
     504           0 :       TEH_plugin->cls,
     505           0 :       &rmc->refresh_session.coin.coin_pub,
     506             :       &denom_hash);
     507           0 :     if (0 > qs)
     508             :     {
     509             :       /* There is no good reason for a serialization failure here: */
     510           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     511           0 :       return TALER_MHD_reply_with_error (connection,
     512             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     513             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     514             :                                          "coin denomination");
     515             :     }
     516             :     /* sanity check */
     517           0 :     GNUNET_break (0 ==
     518             :                   GNUNET_memcmp (&denom_hash,
     519             :                                  &rmc->refresh_session.coin.denom_pub_hash));
     520           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     521             :     {
     522             :       struct GNUNET_TIME_Absolute now;
     523             : 
     524           0 :       now = GNUNET_TIME_absolute_get ();
     525           0 :       (void) GNUNET_TIME_round_abs (&now);
     526             :       /* We never saw this coin before, so _this_ justification is not OK */
     527           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     528             :         connection,
     529           0 :         &rmc->refresh_session.coin.denom_pub_hash,
     530             :         now,
     531             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     532             :         "MELT");
     533             :     }
     534             :     else
     535             :     {
     536             :       /* Minor optimization: no need to run the
     537             :          "ensure_coin_known" part of the transaction */
     538           0 :       rmc->coin_is_dirty = true;
     539             :     }
     540           0 :     rmc->zombie_required = true;   /* check later that zombie is satisfied */
     541             :   }
     542             : 
     543          21 :   rmc->coin_refresh_fee = dk->meta.fee_refresh;
     544          21 :   rmc->coin_value = dk->meta.value;
     545             :   /* check coin is actually properly signed */
     546          21 :   if (GNUNET_OK !=
     547          21 :       TALER_test_coin_valid (&rmc->refresh_session.coin,
     548          21 :                              &dk->denom_pub))
     549             :   {
     550           0 :     GNUNET_break_op (0);
     551           0 :     return TALER_MHD_reply_with_error (connection,
     552             :                                        MHD_HTTP_FORBIDDEN,
     553             :                                        TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
     554             :                                        NULL);
     555             :   }
     556             : 
     557             :   /* sanity-check that "total melt amount > melt fee" */
     558          21 :   if (0 <
     559          21 :       TALER_amount_cmp (&rmc->coin_refresh_fee,
     560          21 :                         &rmc->refresh_session.amount_with_fee))
     561             :   {
     562           0 :     GNUNET_break_op (0);
     563           0 :     return TALER_MHD_reply_with_error (connection,
     564             :                                        MHD_HTTP_BAD_REQUEST,
     565             :                                        TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION,
     566             :                                        NULL);
     567             :   }
     568          21 :   return handle_melt (connection,
     569             :                       rmc);
     570             : }
     571             : 
     572             : 
     573             : /**
     574             :  * Handle a "/coins/$COIN_PUB/melt" request.  Parses the request into the JSON
     575             :  * components and then hands things of to #check_for_denomination_key() to
     576             :  * validate the melted coins, the signature and execute the melt using
     577             :  * handle_melt().
     578             : 
     579             :  * @param connection the MHD connection to handle
     580             :  * @param coin_pub public key of the coin
     581             :  * @param root uploaded JSON data
     582             :  * @return MHD result code
     583             :  */
     584             : MHD_RESULT
     585          21 : TEH_handler_melt (struct MHD_Connection *connection,
     586             :                   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     587             :                   const json_t *root)
     588             : {
     589             :   struct MeltContext rmc;
     590             :   enum GNUNET_GenericReturnValue ret;
     591             :   MHD_RESULT res;
     592             :   struct GNUNET_JSON_Specification spec[] = {
     593          21 :     TALER_JSON_spec_denomination_signature ("denom_sig",
     594             :                                             &rmc.refresh_session.coin.denom_sig),
     595          21 :     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     596             :                                  &rmc.refresh_session.coin.denom_pub_hash),
     597          21 :     GNUNET_JSON_spec_fixed_auto ("confirm_sig",
     598             :                                  &rmc.refresh_session.coin_sig),
     599          21 :     TALER_JSON_spec_amount ("value_with_fee",
     600             :                             TEH_currency,
     601             :                             &rmc.refresh_session.amount_with_fee),
     602          21 :     GNUNET_JSON_spec_fixed_auto ("rc",
     603             :                                  &rmc.refresh_session.rc),
     604          21 :     GNUNET_JSON_spec_end ()
     605             :   };
     606             : 
     607          21 :   memset (&rmc,
     608             :           0,
     609             :           sizeof (rmc));
     610          21 :   rmc.refresh_session.coin.coin_pub = *coin_pub;
     611          21 :   ret = TALER_MHD_parse_json_data (connection,
     612             :                                    root,
     613             :                                    spec);
     614          21 :   if (GNUNET_OK != ret)
     615           0 :     return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
     616             : 
     617          21 :   res = check_for_denomination_key (connection,
     618             :                                     &rmc);
     619          21 :   GNUNET_JSON_parse_free (spec);
     620          21 :   return res;
     621             : }
     622             : 
     623             : 
     624             : /* end of taler-exchange-httpd_melt.c */

Generated by: LCOV version 1.14