LCOV - code coverage report
Current view: top level - lib - exchange_api_melt.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 130 219 59.4 %
Date: 2021-08-30 06:43:37 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU 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 General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file lib/exchange_api_melt.c
      19             :  * @brief Implementation of the /coins/$COIN_PUB/melt request
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <jansson.h>
      24             : #include <microhttpd.h> /* just for HTTP status codes */
      25             : #include <gnunet/gnunet_util_lib.h>
      26             : #include <gnunet/gnunet_json_lib.h>
      27             : #include <gnunet/gnunet_curl_lib.h>
      28             : #include "taler_json_lib.h"
      29             : #include "taler_exchange_service.h"
      30             : #include "exchange_api_handle.h"
      31             : #include "taler_signatures.h"
      32             : #include "exchange_api_curl_defaults.h"
      33             : #include "exchange_api_refresh_common.h"
      34             : 
      35             : 
      36             : /**
      37             :  * @brief A /coins/$COIN_PUB/melt Handle
      38             :  */
      39             : struct TALER_EXCHANGE_MeltHandle
      40             : {
      41             : 
      42             :   /**
      43             :    * The connection to exchange this request handle will use
      44             :    */
      45             :   struct TALER_EXCHANGE_Handle *exchange;
      46             : 
      47             :   /**
      48             :    * The url for this request.
      49             :    */
      50             :   char *url;
      51             : 
      52             :   /**
      53             :    * Context for #TEH_curl_easy_post(). Keeps the data that must
      54             :    * persist for Curl to make the upload.
      55             :    */
      56             :   struct TALER_CURL_PostContext ctx;
      57             : 
      58             :   /**
      59             :    * Handle for the request.
      60             :    */
      61             :   struct GNUNET_CURL_Job *job;
      62             : 
      63             :   /**
      64             :    * Function to call with refresh melt failure results.
      65             :    */
      66             :   TALER_EXCHANGE_MeltCallback melt_cb;
      67             : 
      68             :   /**
      69             :    * Closure for @e result_cb and @e melt_failure_cb.
      70             :    */
      71             :   void *melt_cb_cls;
      72             : 
      73             :   /**
      74             :    * Actual information about the melt operation.
      75             :    */
      76             :   struct MeltData *md;
      77             : 
      78             :   /**
      79             :    * Public key of the coin being melted.
      80             :    */
      81             :   struct TALER_CoinSpendPublicKeyP coin_pub;
      82             : 
      83             :   /**
      84             :    * @brief Public information about the coin's denomination key
      85             :    */
      86             :   struct TALER_EXCHANGE_DenomPublicKey dki;
      87             : };
      88             : 
      89             : 
      90             : /**
      91             :  * Verify that the signature on the "200 OK" response
      92             :  * from the exchange is valid.
      93             :  *
      94             :  * @param mh melt handle
      95             :  * @param json json reply with the signature
      96             :  * @param[out] exchange_pub public key of the exchange used for the signature
      97             :  * @param[out] noreveal_index set to the noreveal index selected by the exchange
      98             :  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
      99             :  */
     100             : static int
     101          17 : verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
     102             :                           const json_t *json,
     103             :                           struct TALER_ExchangePublicKeyP *exchange_pub,
     104             :                           uint32_t *noreveal_index)
     105             : {
     106             :   struct TALER_ExchangeSignatureP exchange_sig;
     107             :   const struct TALER_EXCHANGE_Keys *key_state;
     108             :   struct GNUNET_JSON_Specification spec[] = {
     109          17 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
     110          17 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
     111          17 :     GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index),
     112          17 :     GNUNET_JSON_spec_end ()
     113             :   };
     114             :   struct TALER_RefreshMeltConfirmationPS confirm;
     115             : 
     116          17 :   if (GNUNET_OK !=
     117          17 :       GNUNET_JSON_parse (json,
     118             :                          spec,
     119             :                          NULL, NULL))
     120             :   {
     121           0 :     GNUNET_break_op (0);
     122           0 :     return GNUNET_SYSERR;
     123             :   }
     124             : 
     125             :   /* check that exchange signing key is permitted */
     126          17 :   key_state = TALER_EXCHANGE_get_keys (mh->exchange);
     127          17 :   if (GNUNET_OK !=
     128          17 :       TALER_EXCHANGE_test_signing_key (key_state,
     129             :                                        exchange_pub))
     130             :   {
     131           0 :     GNUNET_break_op (0);
     132           0 :     return GNUNET_SYSERR;
     133             :   }
     134             : 
     135             :   /* check that noreveal index is in permitted range */
     136          17 :   if (TALER_CNC_KAPPA <= *noreveal_index)
     137             :   {
     138           0 :     GNUNET_break_op (0);
     139           0 :     return GNUNET_SYSERR;
     140             :   }
     141             : 
     142             :   /* verify signature by exchange */
     143          17 :   confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
     144          17 :   confirm.purpose.size = htonl (sizeof (struct
     145             :                                         TALER_RefreshMeltConfirmationPS));
     146          17 :   confirm.rc = mh->md->rc;
     147          17 :   confirm.noreveal_index = htonl (*noreveal_index);
     148          17 :   if (GNUNET_OK !=
     149          17 :       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
     150             :                                   &confirm,
     151             :                                   &exchange_sig.eddsa_signature,
     152             :                                   &exchange_pub->eddsa_pub))
     153             :   {
     154           0 :     GNUNET_break_op (0);
     155           0 :     return GNUNET_SYSERR;
     156             :   }
     157          17 :   return GNUNET_OK;
     158             : }
     159             : 
     160             : 
     161             : /**
     162             :  * Verify that the signatures on the "409 CONFLICT" response from the
     163             :  * exchange demonstrating customer denomination key differences
     164             :  * resulting from coin private key reuse are valid.
     165             :  *
     166             :  * @param mh melt handle
     167             :  * @param json json reply with the signature(s) and transaction history
     168             :  * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
     169             :  */
     170             : static int
     171           1 : verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
     172             :                                       const json_t *json)
     173             : 
     174             : {
     175             :   json_t *history;
     176             :   struct TALER_Amount total;
     177             :   struct GNUNET_HashCode h_denom_pub;
     178             : 
     179           1 :   memset (&h_denom_pub,
     180             :           0,
     181             :           sizeof (h_denom_pub));
     182           1 :   history = json_object_get (json,
     183             :                              "history");
     184           1 :   if (GNUNET_OK !=
     185           1 :       TALER_EXCHANGE_verify_coin_history (&mh->dki,
     186           1 :                                           mh->dki.value.currency,
     187           1 :                                           &mh->coin_pub,
     188             :                                           history,
     189             :                                           &h_denom_pub,
     190             :                                           &total))
     191             :   {
     192           0 :     GNUNET_break_op (0);
     193           0 :     return GNUNET_SYSERR;
     194             :   }
     195           1 :   if (0 != GNUNET_memcmp (&mh->dki.h_key,
     196             :                           &h_denom_pub))
     197           1 :     return GNUNET_OK; /* indeed, proof with different denomination key provided */
     198             :   /* invalid proof provided */
     199           0 :   return GNUNET_SYSERR;
     200             : }
     201             : 
     202             : 
     203             : /**
     204             :  * Verify that the signatures on the "409 CONFLICT" response from the
     205             :  * exchange demonstrating customer double-spending are valid.
     206             :  *
     207             :  * @param mh melt handle
     208             :  * @param json json reply with the signature(s) and transaction history
     209             :  * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
     210             :  */
     211             : static int
     212           3 : verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
     213             :                                       const json_t *json)
     214             : {
     215             :   json_t *history;
     216             :   struct TALER_Amount original_value;
     217             :   struct TALER_Amount melt_value_with_fee;
     218             :   struct TALER_Amount total;
     219             :   struct TALER_CoinSpendPublicKeyP coin_pub;
     220             :   struct GNUNET_JSON_Specification spec[] = {
     221           3 :     GNUNET_JSON_spec_json ("history", &history),
     222           3 :     GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
     223           3 :     TALER_JSON_spec_amount_any ("original_value", &original_value),
     224           3 :     TALER_JSON_spec_amount_any ("requested_value", &melt_value_with_fee),
     225           3 :     GNUNET_JSON_spec_end ()
     226             :   };
     227             :   const struct MeltedCoin *mc;
     228             :   enum TALER_ErrorCode ec;
     229             :   struct GNUNET_HashCode h_denom_pub;
     230             : 
     231             :   /* parse JSON reply */
     232           3 :   if (GNUNET_OK !=
     233           3 :       GNUNET_JSON_parse (json,
     234             :                          spec,
     235             :                          NULL, NULL))
     236             :   {
     237           0 :     GNUNET_break_op (0);
     238           0 :     return GNUNET_SYSERR;
     239             :   }
     240             : 
     241             :   /* Find out which coin was deemed problematic by the exchange */
     242           3 :   mc = &mh->md->melted_coin;
     243             : 
     244             :   /* check basic coin properties */
     245           3 :   if (0 != TALER_amount_cmp (&original_value,
     246             :                              &mc->original_value))
     247             :   {
     248             :     /* We disagree on the value of the coin */
     249           0 :     GNUNET_break_op (0);
     250           0 :     json_decref (history);
     251           0 :     return GNUNET_SYSERR;
     252             :   }
     253           3 :   if (0 != TALER_amount_cmp (&melt_value_with_fee,
     254             :                              &mc->melt_amount_with_fee))
     255             :   {
     256             :     /* We disagree on the value of the coin */
     257           0 :     GNUNET_break_op (0);
     258           0 :     json_decref (history);
     259           0 :     return GNUNET_SYSERR;
     260             :   }
     261             : 
     262             :   /* verify coin history */
     263           3 :   memset (&h_denom_pub,
     264             :           0,
     265             :           sizeof (h_denom_pub));
     266           3 :   history = json_object_get (json,
     267             :                              "history");
     268           3 :   if (GNUNET_OK !=
     269           3 :       TALER_EXCHANGE_verify_coin_history (&mh->dki,
     270             :                                           original_value.currency,
     271             :                                           &coin_pub,
     272             :                                           history,
     273             :                                           &h_denom_pub,
     274             :                                           &total))
     275             :   {
     276           0 :     GNUNET_break_op (0);
     277           0 :     json_decref (history);
     278           0 :     return GNUNET_SYSERR;
     279             :   }
     280           3 :   json_decref (history);
     281             : 
     282           3 :   ec = TALER_JSON_get_error_code (json);
     283           3 :   switch (ec)
     284             :   {
     285           3 :   case TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS:
     286             :     /* check if melt operation was really too expensive given history */
     287           3 :     if (0 >
     288           3 :         TALER_amount_add (&total,
     289             :                           &total,
     290             :                           &melt_value_with_fee))
     291             :     {
     292             :       /* clearly not OK if our transaction would have caused
     293             :          the overflow... */
     294           0 :       return GNUNET_OK;
     295             :     }
     296             : 
     297           3 :     if (0 >= TALER_amount_cmp (&total,
     298             :                                &original_value))
     299             :     {
     300             :       /* transaction should have still fit */
     301           0 :       GNUNET_break (0);
     302           0 :       return GNUNET_SYSERR;
     303             :     }
     304             : 
     305             :     /* everything OK, valid proof of double-spending was provided */
     306           3 :     return GNUNET_OK;
     307           0 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
     308           0 :     if (0 != GNUNET_memcmp (&mh->dki.h_key,
     309             :                             &h_denom_pub))
     310           0 :       return GNUNET_OK; /* indeed, proof with different denomination key provided */
     311             :     /* invalid proof provided */
     312           0 :     GNUNET_break_op (0);
     313           0 :     return GNUNET_SYSERR;
     314           0 :   default:
     315             :     /* unexpected error code */
     316           0 :     GNUNET_break_op (0);
     317           0 :     return GNUNET_SYSERR;
     318             :   }
     319             : }
     320             : 
     321             : 
     322             : /**
     323             :  * Function called when we're done processing the
     324             :  * HTTP /coins/$COIN_PUB/melt request.
     325             :  *
     326             :  * @param cls the `struct TALER_EXCHANGE_MeltHandle`
     327             :  * @param response_code HTTP response code, 0 on error
     328             :  * @param response parsed JSON result, NULL on error
     329             :  */
     330             : static void
     331          21 : handle_melt_finished (void *cls,
     332             :                       long response_code,
     333             :                       const void *response)
     334             : {
     335          21 :   struct TALER_EXCHANGE_MeltHandle *mh = cls;
     336          21 :   uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
     337             :   struct TALER_ExchangePublicKeyP exchange_pub;
     338          21 :   const json_t *j = response;
     339          21 :   struct TALER_EXCHANGE_HttpResponse hr = {
     340             :     .reply = j,
     341          21 :     .http_status = (unsigned int) response_code
     342             :   };
     343             : 
     344          21 :   mh->job = NULL;
     345          21 :   switch (response_code)
     346             :   {
     347           0 :   case 0:
     348           0 :     hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     349           0 :     break;
     350          17 :   case MHD_HTTP_OK:
     351          17 :     if (GNUNET_OK !=
     352          17 :         verify_melt_signature_ok (mh,
     353             :                                   j,
     354             :                                   &exchange_pub,
     355             :                                   &noreveal_index))
     356             :     {
     357           0 :       GNUNET_break_op (0);
     358           0 :       hr.http_status = 0;
     359           0 :       hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
     360             :     }
     361          17 :     if (NULL != mh->melt_cb)
     362             :     {
     363          17 :       mh->melt_cb (mh->melt_cb_cls,
     364             :                    &hr,
     365             :                    noreveal_index,
     366          17 :                    (0 == hr.http_status)
     367             :                    ? NULL
     368             :                    : &exchange_pub);
     369          17 :       mh->melt_cb = NULL;
     370             :     }
     371          17 :     break;
     372           0 :   case MHD_HTTP_BAD_REQUEST:
     373             :     /* This should never happen, either us or the exchange is buggy
     374             :        (or API version conflict); just pass JSON reply to the application */
     375           0 :     hr.ec = TALER_JSON_get_error_code (j);
     376           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     377           0 :     break;
     378           4 :   case MHD_HTTP_CONFLICT:
     379           4 :     hr.ec = TALER_JSON_get_error_code (j);
     380           4 :     switch (hr.ec)
     381             :     {
     382           3 :     case TALER_EC_EXCHANGE_MELT_INSUFFICIENT_FUNDS:
     383             :       /* Double spending; check signatures on transaction history */
     384           3 :       if (GNUNET_OK !=
     385           3 :           verify_melt_signature_spend_conflict (mh,
     386             :                                                 j))
     387             :       {
     388           0 :         GNUNET_break_op (0);
     389           0 :         hr.http_status = 0;
     390           0 :         hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
     391           0 :         hr.hint = TALER_JSON_get_error_hint (j);
     392             :       }
     393           3 :       break;
     394           1 :     case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
     395           1 :       if (GNUNET_OK !=
     396           1 :           verify_melt_signature_denom_conflict (mh,
     397             :                                                 j))
     398             :       {
     399           0 :         GNUNET_break_op (0);
     400           0 :         hr.http_status = 0;
     401           0 :         hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
     402           0 :         hr.hint = TALER_JSON_get_error_hint (j);
     403             :       }
     404           1 :       break;
     405           0 :     default:
     406           0 :       GNUNET_break_op (0);
     407           0 :       hr.http_status = 0;
     408           0 :       hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
     409           0 :       hr.hint = TALER_JSON_get_error_hint (j);
     410           0 :       break;
     411             :     }
     412           4 :     break;
     413           0 :   case MHD_HTTP_FORBIDDEN:
     414             :     /* Nothing really to verify, exchange says one of the signatures is
     415             :        invalid; assuming we checked them, this should never happen, we
     416             :        should pass the JSON reply to the application */
     417           0 :     hr.ec = TALER_JSON_get_error_code (j);
     418           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     419           0 :     break;
     420           0 :   case MHD_HTTP_NOT_FOUND:
     421             :     /* Nothing really to verify, this should never
     422             :        happen, we should pass the JSON reply to the application */
     423           0 :     hr.ec = TALER_JSON_get_error_code (j);
     424           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     425           0 :     break;
     426           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     427             :     /* Server had an internal issue; we should retry, but this API
     428             :        leaves this to the application */
     429           0 :     hr.ec = TALER_JSON_get_error_code (j);
     430           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     431           0 :     break;
     432           0 :   default:
     433             :     /* unexpected response code */
     434           0 :     hr.ec = TALER_JSON_get_error_code (j);
     435           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     436           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     437             :                 "Unexpected response code %u/%d for exchange melt\n",
     438             :                 (unsigned int) response_code,
     439             :                 hr.ec);
     440           0 :     GNUNET_break_op (0);
     441           0 :     break;
     442             :   }
     443          21 :   if (NULL != mh->melt_cb)
     444           4 :     mh->melt_cb (mh->melt_cb_cls,
     445             :                  &hr,
     446             :                  UINT32_MAX,
     447             :                  NULL);
     448          21 :   TALER_EXCHANGE_melt_cancel (mh);
     449          21 : }
     450             : 
     451             : 
     452             : struct TALER_EXCHANGE_MeltHandle *
     453          21 : TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
     454             :                      size_t refresh_data_length,
     455             :                      const char *refresh_data,
     456             :                      TALER_EXCHANGE_MeltCallback melt_cb,
     457             :                      void *melt_cb_cls)
     458             : {
     459             :   const struct TALER_EXCHANGE_Keys *key_state;
     460             :   const struct TALER_EXCHANGE_DenomPublicKey *dki;
     461             :   json_t *melt_obj;
     462             :   struct TALER_EXCHANGE_MeltHandle *mh;
     463             :   CURL *eh;
     464             :   struct GNUNET_CURL_Context *ctx;
     465             :   struct MeltData *md;
     466             :   struct TALER_CoinSpendSignatureP confirm_sig;
     467             :   char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
     468          21 :   struct TALER_RefreshMeltCoinAffirmationPS melt = {
     469          21 :     .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT),
     470          21 :     .purpose.size = htonl (sizeof (melt)),
     471             :   };
     472             : 
     473          21 :   GNUNET_assert (GNUNET_YES ==
     474             :                  TEAH_handle_is_ready (exchange));
     475          21 :   md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
     476             :                                               refresh_data_length);
     477          21 :   if (NULL == md)
     478             :   {
     479           0 :     GNUNET_break (0);
     480           0 :     return NULL;
     481             :   }
     482          21 :   melt.rc = md->rc;
     483          21 :   TALER_amount_hton (&melt.amount_with_fee,
     484          21 :                      &md->melted_coin.melt_amount_with_fee);
     485          21 :   TALER_amount_hton (&melt.melt_fee,
     486          21 :                      &md->melted_coin.fee_melt);
     487          21 :   GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
     488             :                                       &melt.coin_pub.eddsa_pub);
     489          21 :   GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
     490             :                                      &melt.h_denom_pub);
     491          21 :   GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
     492             :                             &melt,
     493             :                             &confirm_sig.eddsa_signature);
     494          21 :   melt_obj = GNUNET_JSON_PACK (
     495             :     GNUNET_JSON_pack_data_auto ("coin_pub",
     496             :                                 &melt.coin_pub),
     497             :     GNUNET_JSON_pack_data_auto ("denom_pub_hash",
     498             :                                 &melt.h_denom_pub),
     499             :     TALER_JSON_pack_denomination_signature ("denom_sig",
     500             :                                             &md->melted_coin.sig),
     501             :     GNUNET_JSON_pack_data_auto ("confirm_sig",
     502             :                                 &confirm_sig),
     503             :     TALER_JSON_pack_amount ("value_with_fee",
     504             :                             &md->melted_coin.melt_amount_with_fee),
     505             :     GNUNET_JSON_pack_data_auto ("rc",
     506             :                                 &melt.rc));
     507             :   {
     508             :     char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
     509             :     char *end;
     510             : 
     511          21 :     end = GNUNET_STRINGS_data_to_string (
     512             :       &melt.coin_pub,
     513             :       sizeof (struct TALER_CoinSpendPublicKeyP),
     514             :       pub_str,
     515             :       sizeof (pub_str));
     516          21 :     *end = '\0';
     517          21 :     GNUNET_snprintf (arg_str,
     518             :                      sizeof (arg_str),
     519             :                      "/coins/%s/melt",
     520             :                      pub_str);
     521             :   }
     522             : 
     523          21 :   key_state = TALER_EXCHANGE_get_keys (exchange);
     524          21 :   dki = TALER_EXCHANGE_get_denomination_key (key_state,
     525          21 :                                              &md->melted_coin.pub_key);
     526             : 
     527             :   /* and now we can at last begin the actual request handling */
     528          21 :   mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
     529          21 :   mh->exchange = exchange;
     530          21 :   mh->coin_pub = melt.coin_pub;
     531          21 :   mh->dki = *dki;
     532          21 :   mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
     533             :                                          not copy the pointer */
     534          21 :   mh->melt_cb = melt_cb;
     535          21 :   mh->melt_cb_cls = melt_cb_cls;
     536          21 :   mh->md = md;
     537          21 :   mh->url = TEAH_path_to_url (exchange,
     538             :                               arg_str);
     539          21 :   if (NULL == mh->url)
     540             :   {
     541           0 :     json_decref (melt_obj);
     542           0 :     GNUNET_free (mh);
     543           0 :     return NULL;
     544             :   }
     545          21 :   eh = TALER_EXCHANGE_curl_easy_get_ (mh->url);
     546          42 :   if ( (NULL == eh) ||
     547             :        (GNUNET_OK !=
     548          21 :         TALER_curl_easy_post (&mh->ctx,
     549             :                               eh,
     550             :                               melt_obj)) )
     551             :   {
     552           0 :     GNUNET_break (0);
     553           0 :     if (NULL != eh)
     554           0 :       curl_easy_cleanup (eh);
     555           0 :     json_decref (melt_obj);
     556           0 :     GNUNET_free (mh->url);
     557           0 :     GNUNET_free (mh);
     558           0 :     return NULL;
     559             :   }
     560          21 :   json_decref (melt_obj);
     561          21 :   ctx = TEAH_handle_to_context (exchange);
     562          42 :   mh->job = GNUNET_CURL_job_add2 (ctx,
     563             :                                   eh,
     564          21 :                                   mh->ctx.headers,
     565             :                                   &handle_melt_finished,
     566             :                                   mh);
     567          21 :   return mh;
     568             : }
     569             : 
     570             : 
     571             : void
     572          21 : TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh)
     573             : {
     574          21 :   if (NULL != mh->job)
     575             :   {
     576           0 :     GNUNET_CURL_job_cancel (mh->job);
     577           0 :     mh->job = NULL;
     578             :   }
     579          21 :   TALER_EXCHANGE_free_melt_data_ (mh->md); /* does not free 'md' itself */
     580          21 :   GNUNET_free (mh->md);
     581          21 :   GNUNET_free (mh->url);
     582          21 :   TALER_curl_easy_post_finished (&mh->ctx);
     583          21 :   GNUNET_free (mh);
     584          21 : }
     585             : 
     586             : 
     587             : /* end of exchange_api_melt.c */

Generated by: LCOV version 1.14