LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_recoup.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 101 143 70.6 %
Date: 2021-08-30 06:43:37 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2017-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_recoup.c
      18             :  * @brief Handle /recoup requests; parses the POST and JSON and
      19             :  *        verifies the coin signature before handing things off
      20             :  *        to the database.
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <gnunet/gnunet_json_lib.h>
      26             : #include <jansson.h>
      27             : #include <microhttpd.h>
      28             : #include <pthread.h>
      29             : #include "taler_json_lib.h"
      30             : #include "taler_mhd_lib.h"
      31             : #include "taler-exchange-httpd_recoup.h"
      32             : #include "taler-exchange-httpd_responses.h"
      33             : #include "taler-exchange-httpd_keys.h"
      34             : #include "taler_exchangedb_lib.h"
      35             : 
      36             : /**
      37             :  * Closure for #recoup_transaction.
      38             :  */
      39             : struct RecoupContext
      40             : {
      41             :   /**
      42             :    * Hash of the blinded coin.
      43             :    */
      44             :   struct GNUNET_HashCode h_blind;
      45             : 
      46             :   /**
      47             :    * Full value of the coin.
      48             :    */
      49             :   struct TALER_Amount value;
      50             : 
      51             :   /**
      52             :    * Details about the coin.
      53             :    */
      54             :   const struct TALER_CoinPublicInfo *coin;
      55             : 
      56             :   /**
      57             :    * Key used to blind the coin.
      58             :    */
      59             :   const struct TALER_DenominationBlindingKeyP *coin_bks;
      60             : 
      61             :   /**
      62             :    * Signature of the coin requesting recoup.
      63             :    */
      64             :   const struct TALER_CoinSpendSignatureP *coin_sig;
      65             : 
      66             :   /**
      67             :    * Where does the value of the recouped coin go? Which member
      68             :    * of the union is valid depends on @e refreshed.
      69             :    */
      70             :   union
      71             :   {
      72             :     /**
      73             :      * Set by #recoup_transaction() to the reserve that will
      74             :      * receive the recoup, if #refreshed is #GNUNET_NO.
      75             :      */
      76             :     struct TALER_ReservePublicKeyP reserve_pub;
      77             : 
      78             :     /**
      79             :      * Set by #recoup_transaction() to the old coin that will
      80             :      * receive the recoup, if #refreshed is #GNUNET_YES.
      81             :      */
      82             :     struct TALER_CoinSpendPublicKeyP old_coin_pub;
      83             :   } target;
      84             : 
      85             :   /**
      86             :    * Set by #recoup_transaction() to the amount that will be paid back
      87             :    */
      88             :   struct TALER_Amount amount;
      89             : 
      90             :   /**
      91             :    * Set by #recoup_transaction to the timestamp when the recoup
      92             :    * was accepted.
      93             :    */
      94             :   struct GNUNET_TIME_Absolute now;
      95             : 
      96             :   /**
      97             :    * #GNUNET_YES if the client claims the coin originated from a refresh.
      98             :    */
      99             :   int refreshed;
     100             : 
     101             : };
     102             : 
     103             : 
     104             : /**
     105             :  * Execute a "recoup".  The validity of the coin and signature have
     106             :  * already been checked.  The database must now check that the coin is
     107             :  * not (double) spent, and execute the transaction.
     108             :  *
     109             :  * IF it returns a non-error code, the transaction logic MUST
     110             :  * NOT queue a MHD response.  IF it returns an hard error, the
     111             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     112             :  * it returns the soft error code, the function MAY be called again to
     113             :  * retry and MUST not queue a MHD response.
     114             :  *
     115             :  * @param cls the `struct RecoupContext *`
     116             :  * @param connection MHD request which triggered the transaction
     117             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     118             :  *             if transaction failed (!)
     119             :  * @return transaction status code
     120             :  */
     121             : static enum GNUNET_DB_QueryStatus
     122          17 : recoup_transaction (void *cls,
     123             :                     struct MHD_Connection *connection,
     124             :                     MHD_RESULT *mhd_ret)
     125             : {
     126          17 :   struct RecoupContext *pc = cls;
     127             :   struct TALER_EXCHANGEDB_TransactionList *tl;
     128             :   struct TALER_Amount spent;
     129             :   struct TALER_Amount recouped;
     130             :   enum GNUNET_DB_QueryStatus qs;
     131             :   int existing_recoup_found;
     132             : 
     133             :   /* make sure coin is 'known' in database */
     134          17 :   qs = TEH_make_coin_known (pc->coin,
     135             :                             connection,
     136             :                             mhd_ret);
     137          17 :   if (qs < 0)
     138           1 :     return qs;
     139             : 
     140             :   /* Check whether a recoup is allowed, and if so, to which
     141             :      reserve / account the money should go */
     142          16 :   if (pc->refreshed)
     143             :   {
     144           9 :     qs = TEH_plugin->get_old_coin_by_h_blind (TEH_plugin->cls,
     145           9 :                                               &pc->h_blind,
     146             :                                               &pc->target.old_coin_pub);
     147           9 :     if (0 > qs)
     148             :     {
     149           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     150             :       {
     151           0 :         GNUNET_break (0);
     152           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     153             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     154             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     155             :                                                "old coin by h_blind");
     156             :       }
     157           0 :       return qs;
     158             :     }
     159             :   }
     160             :   else
     161             :   {
     162           7 :     qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
     163           7 :                                              &pc->h_blind,
     164             :                                              &pc->target.reserve_pub);
     165           7 :     if (0 > qs)
     166             :     {
     167           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     168             :       {
     169           0 :         GNUNET_break (0);
     170           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     171             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     172             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     173             :                                                "reserve by h_blind");
     174             :       }
     175           0 :       return qs;
     176             :     }
     177             :   }
     178          16 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     179             :   {
     180           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     181             :                 "Recoup requested for unknown envelope %s\n",
     182             :                 GNUNET_h2s (&pc->h_blind));
     183           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     184             :                                            MHD_HTTP_NOT_FOUND,
     185             :                                            TALER_EC_EXCHANGE_RECOUP_WITHDRAW_NOT_FOUND,
     186             :                                            NULL);
     187           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     188             :   }
     189             : 
     190             :   /* Calculate remaining balance, including recoups already applied. */
     191          16 :   qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
     192          16 :                                           &pc->coin->coin_pub,
     193             :                                           GNUNET_YES,
     194             :                                           &tl);
     195          16 :   if (0 > qs)
     196             :   {
     197           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     198             :     {
     199           0 :       GNUNET_break (0);
     200           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     201             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     202             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     203             :                                              "coin transaction list");
     204             :     }
     205           0 :     return qs;
     206             :   }
     207             : 
     208          16 :   GNUNET_assert (GNUNET_OK ==
     209             :                  TALER_amount_set_zero (pc->value.currency,
     210             :                                         &spent));
     211          16 :   GNUNET_assert (GNUNET_OK ==
     212             :                  TALER_amount_set_zero (pc->value.currency,
     213             :                                         &recouped));
     214             :   /* Check if this coin has been recouped already at least once */
     215          16 :   existing_recoup_found = GNUNET_NO;
     216          26 :   for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
     217             :        NULL != pos;
     218          10 :        pos = pos->next)
     219             :   {
     220          15 :     if ( (TALER_EXCHANGEDB_TT_RECOUP == pos->type) ||
     221          14 :          (TALER_EXCHANGEDB_TT_RECOUP_REFRESH == pos->type) )
     222             :     {
     223           5 :       existing_recoup_found = GNUNET_YES;
     224           5 :       break;
     225             :     }
     226             :   }
     227          16 :   if (GNUNET_OK !=
     228          16 :       TALER_EXCHANGEDB_calculate_transaction_list_totals (tl,
     229             :                                                           &spent,
     230             :                                                           &spent))
     231             :   {
     232           0 :     GNUNET_break (0);
     233           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     234             :                                             tl);
     235           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     236             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     237             :                                            TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     238             :                                            "coin transaction history");
     239           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     240             :   }
     241          16 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     242             :               "Recoup: calculated spent %s\n",
     243             :               TALER_amount2s (&spent));
     244          16 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     245             :               "Recoup: coin value %s\n",
     246             :               TALER_amount2s (&pc->value));
     247          16 :   if (0 >
     248          16 :       TALER_amount_subtract (&pc->amount,
     249          16 :                              &pc->value,
     250             :                              &spent))
     251             :   {
     252           0 :     GNUNET_break (0);
     253           0 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     254             :                                             tl);
     255           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     256             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     257             :                                            TALER_EC_EXCHANGE_RECOUP_COIN_BALANCE_NEGATIVE,
     258             :                                            NULL);
     259           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     260             :   }
     261          16 :   if ( (0 == pc->amount.fraction) &&
     262          12 :        (0 == pc->amount.value) )
     263             :   {
     264             :     /* Recoup has no effect: coin fully spent! */
     265             :     enum GNUNET_DB_QueryStatus ret;
     266             : 
     267           6 :     TEH_plugin->rollback (TEH_plugin->cls);
     268           6 :     if (GNUNET_NO == existing_recoup_found)
     269             :     {
     270             :       /* Refuse: insufficient funds for recoup */
     271           2 :       *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
     272             :                                                              TALER_EC_EXCHANGE_RECOUP_COIN_BALANCE_ZERO,
     273           1 :                                                              &pc->coin->coin_pub,
     274             :                                                              tl);
     275           1 :       ret = GNUNET_DB_STATUS_HARD_ERROR;
     276             :     }
     277             :     else
     278             :     {
     279             :       /* We didn't add any new recoup transaction, but there was at least
     280             :          one recoup before, so we give a success response (idempotency!) */
     281           5 :       ret = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     282             :     }
     283           6 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     284             :                                             tl);
     285           6 :     return ret;
     286             :   }
     287          10 :   TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     288             :                                           tl);
     289          10 :   pc->now = GNUNET_TIME_absolute_get ();
     290          10 :   (void) GNUNET_TIME_round_abs (&pc->now);
     291             : 
     292             :   /* add coin to list of wire transfers for recoup */
     293          10 :   if (pc->refreshed)
     294             :   {
     295           5 :     qs = TEH_plugin->insert_recoup_refresh_request (TEH_plugin->cls,
     296             :                                                     pc->coin,
     297             :                                                     pc->coin_sig,
     298             :                                                     pc->coin_bks,
     299           5 :                                                     &pc->amount,
     300           5 :                                                     &pc->h_blind,
     301             :                                                     pc->now);
     302             :   }
     303             :   else
     304             :   {
     305           5 :     qs = TEH_plugin->insert_recoup_request (TEH_plugin->cls,
     306           5 :                                             &pc->target.reserve_pub,
     307             :                                             pc->coin,
     308             :                                             pc->coin_sig,
     309             :                                             pc->coin_bks,
     310           5 :                                             &pc->amount,
     311           5 :                                             &pc->h_blind,
     312             :                                             pc->now);
     313             :   }
     314          10 :   if (0 > qs)
     315             :   {
     316           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     317             :     {
     318           0 :       TALER_LOG_WARNING ("Failed to store recoup information in database\n");
     319           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     320             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     321             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
     322             :                                              "recoup request");
     323             :     }
     324           0 :     return qs;
     325             :   }
     326          10 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     327             : }
     328             : 
     329             : 
     330             : /**
     331             :  * We have parsed the JSON information about the recoup request. Do
     332             :  * some basic sanity checks (especially that the signature on the
     333             :  * request and coin is valid) and then execute the recoup operation.
     334             :  * Note that we need the DB to check the fee structure, so this is not
     335             :  * done here but during the recoup_transaction().
     336             :  *
     337             :  * @param connection the MHD connection to handle
     338             :  * @param coin information about the coin
     339             :  * @param coin_bks blinding data of the coin (to be checked)
     340             :  * @param coin_sig signature of the coin
     341             :  * @param refreshed #GNUNET_YES if the coin was refreshed
     342             :  * @return MHD result code
     343             :  */
     344             : static MHD_RESULT
     345          18 : verify_and_execute_recoup (struct MHD_Connection *connection,
     346             :                            const struct TALER_CoinPublicInfo *coin,
     347             :                            const struct
     348             :                            TALER_DenominationBlindingKeyP *coin_bks,
     349             :                            const struct TALER_CoinSpendSignatureP *coin_sig,
     350             :                            int refreshed)
     351             : {
     352             :   struct RecoupContext pc;
     353             :   const struct TEH_DenominationKey *dk;
     354             :   struct GNUNET_HashCode c_hash;
     355             :   void *coin_ev;
     356             :   size_t coin_ev_size;
     357             :   MHD_RESULT mret;
     358             : 
     359             :   /* check denomination exists and is in recoup mode */
     360          18 :   dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash,
     361             :                                       connection,
     362             :                                       &mret);
     363          18 :   if (NULL == dk)
     364           0 :     return mret;
     365          18 :   if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit))
     366             :   {
     367             :     struct GNUNET_TIME_Absolute now;
     368             : 
     369           0 :     now = GNUNET_TIME_absolute_get ();
     370           0 :     (void) GNUNET_TIME_round_abs (&now);
     371             :     /* This denomination is past the expiration time for recoup */
     372           0 :     return TEH_RESPONSE_reply_expired_denom_pub_hash (
     373             :       connection,
     374             :       &coin->denom_pub_hash,
     375             :       now,
     376             :       TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     377             :       "RECOUP");
     378             :   }
     379          18 :   if (GNUNET_TIME_absolute_is_future (dk->meta.start))
     380             :   {
     381             :     struct GNUNET_TIME_Absolute now;
     382             : 
     383           0 :     now = GNUNET_TIME_absolute_get ();
     384           0 :     (void) GNUNET_TIME_round_abs (&now);
     385             :     /* This denomination is not yet valid */
     386           0 :     return TEH_RESPONSE_reply_expired_denom_pub_hash (
     387             :       connection,
     388             :       &coin->denom_pub_hash,
     389             :       now,
     390             :       TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     391             :       "RECOUP");
     392             :   }
     393          18 :   if (! dk->recoup_possible)
     394             :   {
     395             :     struct GNUNET_TIME_Absolute now;
     396             : 
     397           1 :     now = GNUNET_TIME_absolute_get ();
     398           1 :     (void) GNUNET_TIME_round_abs (&now);
     399             :     /* This denomination is not eligible for recoup */
     400           1 :     return TEH_RESPONSE_reply_expired_denom_pub_hash (
     401             :       connection,
     402             :       &coin->denom_pub_hash,
     403             :       now,
     404             :       TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE,
     405             :       "RECOUP");
     406             :   }
     407             : 
     408          17 :   pc.value = dk->meta.value;
     409             : 
     410             :   /* check denomination signature */
     411          17 :   if (GNUNET_YES !=
     412          17 :       TALER_test_coin_valid (coin,
     413             :                              &dk->denom_pub))
     414             :   {
     415           0 :     TALER_LOG_WARNING ("Invalid coin passed for recoup\n");
     416           0 :     return TALER_MHD_reply_with_error (connection,
     417             :                                        MHD_HTTP_FORBIDDEN,
     418             :                                        TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
     419             :                                        NULL);
     420             :   }
     421             : 
     422             :   /* check recoup request signature */
     423             :   {
     424          17 :     struct TALER_RecoupRequestPS pr = {
     425          17 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
     426          17 :       .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
     427             :       .coin_pub = coin->coin_pub,
     428             :       .h_denom_pub = coin->denom_pub_hash,
     429             :       .coin_blind = *coin_bks
     430             :     };
     431             : 
     432          17 :     if (GNUNET_OK !=
     433          17 :         GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
     434             :                                     &pr,
     435             :                                     &coin_sig->eddsa_signature,
     436             :                                     &coin->coin_pub.eddsa_pub))
     437             :     {
     438           0 :       TALER_LOG_WARNING ("Invalid signature on recoup request\n");
     439           0 :       return TALER_MHD_reply_with_error (connection,
     440             :                                          MHD_HTTP_FORBIDDEN,
     441             :                                          TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID,
     442             :                                          NULL);
     443             :     }
     444             :   }
     445          17 :   GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub,
     446             :                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
     447             :                       &c_hash);
     448          17 :   if (GNUNET_YES !=
     449          17 :       TALER_rsa_blind (&c_hash,
     450             :                        &coin_bks->bks,
     451             :                        dk->denom_pub.rsa_public_key,
     452             :                        &coin_ev,
     453             :                        &coin_ev_size))
     454             :   {
     455           0 :     GNUNET_break (0);
     456           0 :     return TALER_MHD_reply_with_error (connection,
     457             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     458             :                                        TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
     459             :                                        NULL);
     460             :   }
     461          17 :   GNUNET_CRYPTO_hash (coin_ev,
     462             :                       coin_ev_size,
     463             :                       &pc.h_blind);
     464          17 :   GNUNET_free (coin_ev);
     465             : 
     466             :   /* Perform actual recoup transaction */
     467          17 :   pc.coin_sig = coin_sig;
     468          17 :   pc.coin_bks = coin_bks;
     469          17 :   pc.coin = coin;
     470          17 :   pc.refreshed = refreshed;
     471             :   {
     472             :     MHD_RESULT mhd_ret;
     473             : 
     474          17 :     if (GNUNET_OK !=
     475          17 :         TEH_DB_run_transaction (connection,
     476             :                                 "run recoup",
     477             :                                 &mhd_ret,
     478             :                                 &recoup_transaction,
     479             :                                 &pc))
     480           2 :       return mhd_ret;
     481             :   }
     482             :   /* Recoup succeeded, return result */
     483             :   return (refreshed)
     484           9 :          ? TALER_MHD_REPLY_JSON_PACK (connection,
     485             :                                       MHD_HTTP_OK,
     486             :                                       GNUNET_JSON_pack_data_auto (
     487             :                                         "old_coin_pub",
     488             :                                         &pc.target.old_coin_pub),
     489             :                                       GNUNET_JSON_pack_bool ("refreshed",
     490             :                                                              true))
     491          24 :          : TALER_MHD_REPLY_JSON_PACK (connection,
     492             :                                       MHD_HTTP_OK,
     493             :                                       GNUNET_JSON_pack_data_auto (
     494             :                                         "reserve_pub",
     495             :                                         &pc.target.reserve_pub),
     496             :                                       GNUNET_JSON_pack_bool ("refreshed",
     497             :                                                              false));
     498             : }
     499             : 
     500             : 
     501             : /**
     502             :  * Handle a "/coins/$COIN_PUB/recoup" request.  Parses the JSON, and, if
     503             :  * successful, passes the JSON data to #verify_and_execute_recoup() to further
     504             :  * check the details of the operation specified.  If everything checks out,
     505             :  * this will ultimately lead to the refund being executed, or rejected.
     506             :  *
     507             :  * @param connection the MHD connection to handle
     508             :  * @param coin_pub public key of the coin
     509             :  * @param root uploaded JSON data
     510             :  * @return MHD result code
     511             :   */
     512             : MHD_RESULT
     513          18 : TEH_handler_recoup (struct MHD_Connection *connection,
     514             :                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
     515             :                     const json_t *root)
     516             : {
     517             :   enum GNUNET_GenericReturnValue ret;
     518             :   struct TALER_CoinPublicInfo coin;
     519             :   struct TALER_DenominationBlindingKeyP coin_bks;
     520             :   struct TALER_CoinSpendSignatureP coin_sig;
     521          18 :   int refreshed = GNUNET_NO;
     522             :   struct GNUNET_JSON_Specification spec[] = {
     523          18 :     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     524             :                                  &coin.denom_pub_hash),
     525          18 :     TALER_JSON_spec_denomination_signature ("denom_sig",
     526             :                                             &coin.denom_sig),
     527          18 :     GNUNET_JSON_spec_fixed_auto ("coin_blind_key_secret",
     528             :                                  &coin_bks),
     529          18 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     530             :                                  &coin_sig),
     531          18 :     GNUNET_JSON_spec_mark_optional
     532             :       (GNUNET_JSON_spec_boolean ("refreshed",
     533             :                                  &refreshed)),
     534          18 :     GNUNET_JSON_spec_end ()
     535             :   };
     536             : 
     537          18 :   coin.coin_pub = *coin_pub;
     538          18 :   ret = TALER_MHD_parse_json_data (connection,
     539             :                                    root,
     540             :                                    spec);
     541          18 :   if (GNUNET_SYSERR == ret)
     542           0 :     return MHD_NO; /* hard failure */
     543          18 :   if (GNUNET_NO == ret)
     544           0 :     return MHD_YES; /* failure */
     545             :   {
     546             :     MHD_RESULT res;
     547             : 
     548          18 :     res = verify_and_execute_recoup (connection,
     549             :                                      &coin,
     550             :                                      &coin_bks,
     551             :                                      &coin_sig,
     552             :                                      refreshed);
     553          18 :     GNUNET_JSON_parse_free (spec);
     554          18 :     return res;
     555             :   }
     556             : }
     557             : 
     558             : 
     559             : /* end of taler-exchange-httpd_recoup.c */

Generated by: LCOV version 1.14