LCOV - code coverage report
Current view: top level - exchange-lib - exchange_api_reserve.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 223 370 60.3 %
Date: 2017-11-25 11:31:41 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015 GNUnet e.V.
       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 exchange-lib/exchange_api_reserve.c
      19             :  * @brief Implementation of the /reserve requests of the exchange's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_json_lib.h>
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler_exchange_service.h"
      30             : #include "taler_json_lib.h"
      31             : #include "exchange_api_handle.h"
      32             : #include "taler_signatures.h"
      33             : 
      34             : 
      35             : /* ********************** /reserve/status ********************** */
      36             : 
      37             : /**
      38             :  * @brief A Withdraw Status Handle
      39             :  */
      40             : struct TALER_EXCHANGE_ReserveStatusHandle
      41             : {
      42             : 
      43             :   /**
      44             :    * The connection to exchange this request handle will use
      45             :    */
      46             :   struct TALER_EXCHANGE_Handle *exchange;
      47             : 
      48             :   /**
      49             :    * The url for this request.
      50             :    */
      51             :   char *url;
      52             : 
      53             :   /**
      54             :    * Handle for the request.
      55             :    */
      56             :   struct GNUNET_CURL_Job *job;
      57             : 
      58             :   /**
      59             :    * Function to call with the result.
      60             :    */
      61             :   TALER_EXCHANGE_ReserveStatusResultCallback cb;
      62             : 
      63             :   /**
      64             :    * Public key of the reserve we are querying.
      65             :    */
      66             :   struct TALER_ReservePublicKeyP reserve_pub;
      67             : 
      68             :   /**
      69             :    * Closure for @a cb.
      70             :    */
      71             :   void *cb_cls;
      72             : 
      73             : };
      74             : 
      75             : 
      76             : /**
      77             :  * Parse history given in JSON format and return it in binary
      78             :  * format.
      79             :  *
      80             :  * @param exchange connection to the exchange we can use
      81             :  * @param history JSON array with the history
      82             :  * @param reserve_pub public key of the reserve to inspect
      83             :  * @param currency currency we expect the balance to be in
      84             :  * @param[out] balance final balance
      85             :  * @param history_length number of entries in @a history
      86             :  * @param[out] rhistory array of length @a history_length, set to the
      87             :  *             parsed history entries
      88             :  * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
      89             :  *         were set,
      90             :  *         #GNUNET_SYSERR if there was a protocol violation in @a history
      91             :  */
      92             : static int
      93           3 : parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
      94             :                        const json_t *history,
      95             :                        const struct TALER_ReservePublicKeyP *reserve_pub,
      96             :                        const char *currency,
      97             :                        struct TALER_Amount *balance,
      98             :                        unsigned int history_length,
      99             :                        struct TALER_EXCHANGE_ReserveHistory *rhistory)
     100           3 : {
     101           3 :   struct GNUNET_HashCode uuid[history_length];
     102             :   unsigned int uuid_off;
     103             :   struct TALER_Amount total_in;
     104             :   struct TALER_Amount total_out;
     105             :   size_t off;
     106             : 
     107           3 :   GNUNET_assert (GNUNET_OK ==
     108             :                  TALER_amount_get_zero (currency,
     109             :                                         &total_in));
     110           3 :   GNUNET_assert (GNUNET_OK ==
     111             :                  TALER_amount_get_zero (currency,
     112             :                                         &total_out));
     113           3 :   uuid_off = 0;
     114          20 :   for (off=0;off<history_length;off++)
     115             :   {
     116             :     json_t *transaction;
     117             :     struct TALER_Amount amount;
     118             :     const char *type;
     119           7 :     struct GNUNET_JSON_Specification hist_spec[] = {
     120             :       GNUNET_JSON_spec_string ("type", &type),
     121             :       TALER_JSON_spec_amount ("amount",
     122             :                               &amount),
     123             :       /* 'wire' and 'signature' are optional depending on 'type'! */
     124             :       GNUNET_JSON_spec_end()
     125             :     };
     126             : 
     127           7 :     transaction = json_array_get (history,
     128             :                                   off);
     129           7 :     if (GNUNET_OK !=
     130           7 :         GNUNET_JSON_parse (transaction,
     131             :                            hist_spec,
     132             :                            NULL, NULL))
     133             :     {
     134           0 :       GNUNET_break_op (0);
     135           0 :       return GNUNET_SYSERR;
     136             :     }
     137           7 :     rhistory[off].amount = amount;
     138             : 
     139           7 :     if (0 == strcasecmp (type,
     140             :                          "DEPOSIT"))
     141             :     {
     142             :       json_t *wire_account;
     143             :       void *wire_reference;
     144             :       size_t wire_reference_size;
     145             : 
     146           3 :       struct GNUNET_JSON_Specification withdraw_spec[] = {
     147             :         GNUNET_JSON_spec_varsize ("wire_reference",
     148             :                                   &wire_reference,
     149             :                                   &wire_reference_size),
     150             :         GNUNET_JSON_spec_json ("sender_account_details",
     151             :                                &wire_account),
     152             :         GNUNET_JSON_spec_end()
     153             :       };
     154             : 
     155           3 :       rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
     156           3 :       if (GNUNET_OK !=
     157           3 :           TALER_amount_add (&total_in,
     158             :                             &total_in,
     159             :                             &amount))
     160             :       {
     161             :         /* overflow in history already!? inconceivable! Bad exchange! */
     162           0 :         GNUNET_break_op (0);
     163           0 :         return GNUNET_SYSERR;
     164             :       }
     165           3 :       if (GNUNET_OK !=
     166           3 :           GNUNET_JSON_parse (transaction,
     167             :                              withdraw_spec,
     168             :                              NULL, NULL))
     169             :       {
     170           0 :         GNUNET_break_op (0);
     171           0 :         return GNUNET_SYSERR;
     172             :       }
     173           3 :       rhistory[off].details.in_details.sender_account_details = wire_account;
     174           3 :       rhistory[off].details.in_details.wire_reference = wire_reference;
     175           3 :       rhistory[off].details.in_details.wire_reference_size = wire_reference_size;
     176             :       /* end type==DEPOSIT */
     177             :     }
     178           4 :     else if (0 == strcasecmp (type,
     179             :                               "WITHDRAW"))
     180             :     {
     181             :       struct TALER_ReserveSignatureP sig;
     182             :       struct TALER_WithdrawRequestPS withdraw_purpose;
     183           3 :       struct GNUNET_JSON_Specification withdraw_spec[] = {
     184             :         GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     185             :                                      &sig),
     186             :         TALER_JSON_spec_amount_nbo ("withdraw_fee",
     187             :                                     &withdraw_purpose.withdraw_fee),
     188             :         GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     189             :                                      &withdraw_purpose.h_denomination_pub),
     190             :         GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
     191             :                                      &withdraw_purpose.h_coin_envelope),
     192             :         GNUNET_JSON_spec_end()
     193             :       };
     194             :       unsigned int i;
     195             : 
     196           3 :       rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     197           3 :       if (GNUNET_OK !=
     198           3 :           GNUNET_JSON_parse (transaction,
     199             :                              withdraw_spec,
     200             :                              NULL, NULL))
     201             :       {
     202           0 :         GNUNET_break_op (0);
     203           0 :         return GNUNET_SYSERR;
     204             :       }
     205             :       withdraw_purpose.purpose.size
     206           3 :         = htonl (sizeof (withdraw_purpose));
     207             :       withdraw_purpose.purpose.purpose
     208           3 :         = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
     209           3 :       withdraw_purpose.reserve_pub = *reserve_pub;
     210           3 :       TALER_amount_hton (&withdraw_purpose.amount_with_fee,
     211             :                          &amount);
     212             :       /* Check that the signature is a valid withdraw request */
     213           3 :       if (GNUNET_OK !=
     214           3 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
     215             :                                       &withdraw_purpose.purpose,
     216             :                                       &sig.eddsa_signature,
     217             :                                       &reserve_pub->eddsa_pub))
     218             :       {
     219           0 :         GNUNET_break_op (0);
     220           0 :         GNUNET_JSON_parse_free (withdraw_spec);
     221           0 :         return GNUNET_SYSERR;
     222             :       }
     223             :       /* TODO: check that withdraw fee matches expectations! */
     224           3 :       rhistory[off].details.out_authorization_sig
     225           3 :         = json_object_get (transaction,
     226             :                            "signature");
     227             :       /* Check check that the same withdraw transaction
     228             :          isn't listed twice by the exchange. We use the
     229             :          "uuid" array to remember the hashes of all
     230             :          purposes, and compare the hashes to find
     231             :          duplicates. */
     232           6 :       GNUNET_CRYPTO_hash (&withdraw_purpose,
     233           3 :                           ntohl (withdraw_purpose.purpose.size),
     234             :                           &uuid[uuid_off]);
     235           3 :       for (i=0;i<uuid_off;i++)
     236             :       {
     237           0 :         if (0 == memcmp (&uuid[uuid_off],
     238           0 :                          &uuid[i],
     239             :                          sizeof (struct GNUNET_HashCode)))
     240             :         {
     241           0 :           GNUNET_break_op (0);
     242           0 :           GNUNET_JSON_parse_free (withdraw_spec);
     243           0 :           return GNUNET_SYSERR;
     244             :         }
     245             :       }
     246           3 :       uuid_off++;
     247             : 
     248           3 :       if (GNUNET_OK !=
     249           3 :           TALER_amount_add (&total_out,
     250             :                             &total_out,
     251             :                             &amount))
     252             :       {
     253             :         /* overflow in history already!? inconceivable! Bad exchange! */
     254           0 :         GNUNET_break_op (0);
     255           0 :         GNUNET_JSON_parse_free (withdraw_spec);
     256           0 :         return GNUNET_SYSERR;
     257             :       }
     258             :       /* end type==WITHDRAW */
     259             :     }
     260           1 :     else if (0 == strcasecmp (type,
     261             :                               "PAYBACK"))
     262             :     {
     263             :       struct TALER_PaybackConfirmationPS pc;
     264             :       struct GNUNET_TIME_Absolute timestamp;
     265             :       const struct TALER_EXCHANGE_Keys *key_state;
     266           3 :       struct GNUNET_JSON_Specification payback_spec[] = {
     267             :         GNUNET_JSON_spec_fixed_auto ("coin_pub",
     268             :                                      &pc.coin_pub),
     269           1 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     270             :                                      &rhistory[off].details.payback_details.exchange_sig),
     271           1 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     272             :                                      &rhistory[off].details.payback_details.exchange_pub),
     273             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     274             :                                             &pc.timestamp),
     275             :         GNUNET_JSON_spec_end()
     276             :       };
     277             : 
     278           1 :       rhistory[off].type = TALER_EXCHANGE_RTT_PAYBACK;
     279           1 :       rhistory[off].amount = amount;
     280           1 :       if (GNUNET_OK !=
     281           1 :           GNUNET_JSON_parse (transaction,
     282             :                              payback_spec,
     283             :                              NULL, NULL))
     284             :       {
     285           0 :         GNUNET_break_op (0);
     286           0 :         return GNUNET_SYSERR;
     287             :       }
     288           1 :       rhistory[off].details.payback_details.coin_pub = pc.coin_pub;
     289           1 :       TALER_amount_hton (&pc.payback_amount,
     290             :                          &amount);
     291           1 :       pc.purpose.size = htonl (sizeof (pc));
     292           1 :       pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
     293           1 :       pc.reserve_pub = *reserve_pub;
     294           1 :       timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
     295           1 :       rhistory[off].details.payback_details.timestamp = timestamp;
     296             : 
     297           1 :       key_state = TALER_EXCHANGE_get_keys (exchange);
     298           1 :       if (GNUNET_OK !=
     299           1 :           TALER_EXCHANGE_test_signing_key (key_state,
     300           1 :                                            &rhistory[off].details.payback_details.exchange_pub))
     301             :       {
     302           0 :         GNUNET_break_op (0);
     303           0 :         return GNUNET_SYSERR;
     304             :       }
     305           1 :       if (GNUNET_OK !=
     306           1 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
     307             :                                       &pc.purpose,
     308           1 :                                       &rhistory[off].details.payback_details.exchange_sig.eddsa_signature,
     309           1 :                                       &rhistory[off].details.payback_details.exchange_pub.eddsa_pub))
     310             :       {
     311           0 :         GNUNET_break_op (0);
     312           0 :         return GNUNET_SYSERR;
     313             :       }
     314           1 :       if (GNUNET_OK !=
     315           1 :           TALER_amount_add (&total_in,
     316             :                             &total_in,
     317           1 :                             &rhistory[off].amount))
     318             :       {
     319             :         /* overflow in history already!? inconceivable! Bad exchange! */
     320           0 :         GNUNET_break_op (0);
     321           0 :         return GNUNET_SYSERR;
     322             :       }
     323             :       /* end type==PAYBACK */
     324             :     }
     325           0 :     else if (0 == strcasecmp (type,
     326             :                               "CLOSING"))
     327             :     {
     328             :       const struct TALER_EXCHANGE_Keys *key_state;
     329             :       struct TALER_ReserveCloseConfirmationPS rcc;
     330             :       struct GNUNET_TIME_Absolute timestamp;
     331           0 :       struct GNUNET_JSON_Specification closing_spec[] = {
     332           0 :         GNUNET_JSON_spec_json ("receiver_account_details",
     333           0 :                                &rhistory[off].details.close_details.receiver_account_details),
     334           0 :         GNUNET_JSON_spec_fixed_auto ("wtid",
     335             :                                      &rhistory[off].details.close_details.wtid),
     336           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     337             :                                      &rhistory[off].details.close_details.exchange_sig),
     338           0 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     339             :                                      &rhistory[off].details.close_details.exchange_pub),
     340             :         TALER_JSON_spec_amount_nbo ("closing_fee",
     341             :                                     &rcc.closing_fee),
     342             :         GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
     343             :                                             &rcc.timestamp),
     344             :         GNUNET_JSON_spec_end()
     345             :       };
     346             : 
     347           0 :       rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
     348           0 :       rhistory[off].amount = amount;
     349           0 :       if (GNUNET_OK !=
     350           0 :           GNUNET_JSON_parse (transaction,
     351             :                              closing_spec,
     352             :                              NULL, NULL))
     353             :       {
     354           0 :         GNUNET_break_op (0);
     355           0 :         return GNUNET_SYSERR;
     356             :       }
     357           0 :       TALER_amount_hton (&rcc.closing_amount,
     358             :                          &amount);
     359           0 :       if (GNUNET_OK !=
     360           0 :           TALER_JSON_hash (rhistory[off].details.close_details.receiver_account_details,
     361             :                            &rcc.h_wire))
     362             :       {
     363           0 :         GNUNET_break (0);
     364           0 :         return GNUNET_SYSERR;
     365             :       }
     366           0 :       rcc.wtid = rhistory[off].details.close_details.wtid;
     367           0 :       rcc.purpose.size = htonl (sizeof (rcc));
     368           0 :       rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
     369           0 :       rcc.reserve_pub = *reserve_pub;
     370           0 :       timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
     371           0 :       rhistory[off].details.close_details.timestamp = timestamp;
     372             : 
     373           0 :       key_state = TALER_EXCHANGE_get_keys (exchange);
     374           0 :       if (GNUNET_OK !=
     375           0 :           TALER_EXCHANGE_test_signing_key (key_state,
     376           0 :                                            &rhistory[off].details.payback_details.exchange_pub))
     377             :       {
     378           0 :         GNUNET_break_op (0);
     379           0 :         return GNUNET_SYSERR;
     380             :       }
     381           0 :       if (GNUNET_OK !=
     382           0 :           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
     383             :                                       &rcc.purpose,
     384           0 :                                       &rhistory[off].details.payback_details.exchange_sig.eddsa_signature,
     385           0 :                                       &rhistory[off].details.payback_details.exchange_pub.eddsa_pub))
     386             :       {
     387           0 :         GNUNET_break_op (0);
     388           0 :         return GNUNET_SYSERR;
     389             :       }
     390           0 :       if (GNUNET_OK !=
     391           0 :           TALER_amount_add (&total_out,
     392             :                             &total_out,
     393           0 :                             &rhistory[off].amount))
     394             :       {
     395             :         /* overflow in history already!? inconceivable! Bad exchange! */
     396           0 :         GNUNET_break_op (0);
     397           0 :         return GNUNET_SYSERR;
     398             :       }
     399             :       /* end type==CLOSING */
     400             :     }
     401             :     else
     402             :     {
     403             :       /* unexpected 'type', protocol incompatibility, complain! */
     404           0 :       GNUNET_break_op (0);
     405           0 :       return GNUNET_SYSERR;
     406             :     }
     407             :   }
     408             : 
     409             :   /* check balance = total_in - total_out < withdraw-amount */
     410           3 :   if (GNUNET_SYSERR ==
     411           3 :       TALER_amount_subtract (balance,
     412             :                              &total_in,
     413             :                              &total_out))
     414             :   {
     415             :     /* total_in < total_out, why did the exchange ever allow this!? */
     416           0 :     GNUNET_break_op (0);
     417           0 :     return GNUNET_SYSERR;
     418             :   }
     419           3 :   return GNUNET_OK;
     420             : }
     421             : 
     422             : 
     423             : /**
     424             :  * Free memory (potentially) allocated by #parse_reserve_history().
     425             :  *
     426             :  * @param rhistory result to free
     427             :  * @param len number of entries in @a rhistory
     428             :  */
     429             : static void
     430           3 : free_rhistory (struct TALER_EXCHANGE_ReserveHistory *rhistory,
     431             :                unsigned int len)
     432             : {
     433          10 :   for (unsigned int i=0;i<len;i++)
     434             :   {
     435           7 :     switch (rhistory[i].type)
     436             :     {
     437             :     case TALER_EXCHANGE_RTT_DEPOSIT:
     438           3 :       GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
     439           3 :       if (NULL != rhistory[i].details.in_details.sender_account_details)
     440           3 :         json_decref (rhistory[i].details.in_details.sender_account_details);
     441           3 :       break;
     442             :     case TALER_EXCHANGE_RTT_WITHDRAWAL:
     443           3 :       break;
     444             :     case TALER_EXCHANGE_RTT_PAYBACK:
     445           1 :       break;
     446             :     case TALER_EXCHANGE_RTT_CLOSE:
     447           0 :       if (NULL != rhistory[i].details.close_details.receiver_account_details)
     448           0 :         json_decref (rhistory[i].details.close_details.receiver_account_details);
     449           0 :       break;
     450             :     }
     451             :   }
     452           3 : }
     453             : 
     454             : 
     455             : /**
     456             :  * Function called when we're done processing the
     457             :  * HTTP /reserve/status request.
     458             :  *
     459             :  * @param cls the `struct TALER_EXCHANGE_ReserveStatusHandle`
     460             :  * @param response_code HTTP response code, 0 on error
     461             :  * @param json parsed JSON result, NULL on error
     462             :  */
     463             : static void
     464           2 : handle_reserve_status_finished (void *cls,
     465             :                                 long response_code,
     466             :                                 const json_t *json)
     467             : {
     468           2 :   struct TALER_EXCHANGE_ReserveStatusHandle *rsh = cls;
     469             : 
     470           2 :   rsh->job = NULL;
     471           2 :   switch (response_code)
     472             :   {
     473             :   case 0:
     474           0 :     break;
     475             :   case MHD_HTTP_OK:
     476             :     {
     477             :       /* TODO: move into separate function... */
     478             :       json_t *history;
     479             :       unsigned int len;
     480             :       struct TALER_Amount balance;
     481             :       struct TALER_Amount balance_from_history;
     482           2 :       struct GNUNET_JSON_Specification spec[] = {
     483             :         TALER_JSON_spec_amount ("balance", &balance),
     484             :         GNUNET_JSON_spec_end()
     485             :       };
     486             : 
     487           2 :       if (GNUNET_OK !=
     488           2 :           GNUNET_JSON_parse (json,
     489             :                              spec,
     490             :                              NULL,
     491             :                              NULL))
     492             :       {
     493           0 :         GNUNET_break_op (0);
     494           0 :         response_code = 0;
     495           0 :         break;
     496             :       }
     497           2 :       history = json_object_get (json,
     498             :                                  "history");
     499           2 :       if (NULL == history)
     500             :       {
     501           0 :         GNUNET_break_op (0);
     502           0 :         response_code = 0;
     503           0 :         break;
     504             :       }
     505           2 :       len = json_array_size (history);
     506           2 :       {
     507           2 :         struct TALER_EXCHANGE_ReserveHistory rhistory[len];
     508             : 
     509           2 :         memset (rhistory, 0, sizeof (rhistory));
     510           2 :         if (GNUNET_OK !=
     511           4 :             parse_reserve_history (rsh->exchange,
     512             :                                    history,
     513           2 :                                    &rsh->reserve_pub,
     514             :                                    balance.currency,
     515             :                                    &balance_from_history,
     516             :                                    len,
     517             :                                    rhistory))
     518             :         {
     519           0 :           GNUNET_break_op (0);
     520           0 :           response_code = 0;
     521             :         }
     522           4 :         if ( (0 != response_code) &&
     523             :              (0 !=
     524           2 :               TALER_amount_cmp (&balance_from_history,
     525             :                                 &balance)) )
     526             :         {
     527             :           /* exchange cannot add up balances!? */
     528           0 :           GNUNET_break_op (0);
     529           0 :           response_code = 0;
     530             :         }
     531           2 :         if (0 != response_code)
     532             :         {
     533           2 :           rsh->cb (rsh->cb_cls,
     534             :                    response_code,
     535             :                    TALER_EC_NONE,
     536             :                    json,
     537             :                    &balance,
     538             :                    len,
     539             :                    rhistory);
     540           2 :           rsh->cb = NULL;
     541             :         }
     542           2 :         free_rhistory (rhistory,
     543             :                        len);
     544             :       }
     545             :     }
     546           2 :     break;
     547             :   case MHD_HTTP_BAD_REQUEST:
     548             :     /* This should never happen, either us or the exchange is buggy
     549             :        (or API version conflict); just pass JSON reply to the application */
     550           0 :     break;
     551             :   case MHD_HTTP_NOT_FOUND:
     552             :     /* Nothing really to verify, this should never
     553             :        happen, we should pass the JSON reply to the application */
     554           0 :     break;
     555             :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     556             :     /* Server had an internal issue; we should retry, but this API
     557             :        leaves this to the application */
     558           0 :     break;
     559             :   default:
     560             :     /* unexpected response code */
     561           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     562             :                 "Unexpected response code %u\n",
     563             :                 (unsigned int) response_code);
     564           0 :     GNUNET_break (0);
     565           0 :     response_code = 0;
     566           0 :     break;
     567             :   }
     568           2 :   if (NULL != rsh->cb)
     569           0 :     rsh->cb (rsh->cb_cls,
     570             :              response_code,
     571             :              TALER_JSON_get_error_code (json),
     572             :              json,
     573             :              NULL,
     574             :              0, NULL);
     575           2 :   TALER_EXCHANGE_reserve_status_cancel (rsh);
     576           2 : }
     577             : 
     578             : 
     579             : /**
     580             :  * Submit a request to obtain the transaction history of a reserve
     581             :  * from the exchange.  Note that while we return the full response to the
     582             :  * caller for further processing, we do already verify that the
     583             :  * response is well-formed (i.e. that signatures included in the
     584             :  * response are all valid and add up to the balance).  If the exchange's
     585             :  * reply is not well-formed, we return an HTTP status code of zero to
     586             :  * @a cb.
     587             :  *
     588             :  * @param exchange the exchange handle; the exchange must be ready to operate
     589             :  * @param reserve_pub public key of the reserve to inspect
     590             :  * @param cb the callback to call when a reply for this request is available
     591             :  * @param cb_cls closure for the above callback
     592             :  * @return a handle for this request; NULL if the inputs are invalid (i.e.
     593             :  *         signatures fail to verify).  In this case, the callback is not called.
     594             :  */
     595             : struct TALER_EXCHANGE_ReserveStatusHandle *
     596           2 : TALER_EXCHANGE_reserve_status (struct TALER_EXCHANGE_Handle *exchange,
     597             :                                const struct TALER_ReservePublicKeyP *reserve_pub,
     598             :                                TALER_EXCHANGE_ReserveStatusResultCallback cb,
     599             :                                void *cb_cls)
     600             : {
     601             :   struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
     602             :   struct GNUNET_CURL_Context *ctx;
     603             :   CURL *eh;
     604             :   char *pub_str;
     605             :   char *arg_str;
     606             : 
     607           2 :   if (GNUNET_YES !=
     608           2 :       MAH_handle_is_ready (exchange))
     609             :   {
     610           0 :     GNUNET_break (0);
     611           0 :     return NULL;
     612             :   }
     613           2 :   pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub,
     614             :                                                  sizeof (struct TALER_ReservePublicKeyP));
     615           2 :   GNUNET_asprintf (&arg_str,
     616             :                    "/reserve/status?reserve_pub=%s",
     617             :                    pub_str);
     618           2 :   GNUNET_free (pub_str);
     619           2 :   rsh = GNUNET_new (struct TALER_EXCHANGE_ReserveStatusHandle);
     620           2 :   rsh->exchange = exchange;
     621           2 :   rsh->cb = cb;
     622           2 :   rsh->cb_cls = cb_cls;
     623           2 :   rsh->reserve_pub = *reserve_pub;
     624           2 :   rsh->url = MAH_path_to_url (exchange,
     625             :                               arg_str);
     626           2 :   GNUNET_free (arg_str);
     627             : 
     628           2 :   eh = curl_easy_init ();
     629           2 :   GNUNET_assert (CURLE_OK ==
     630             :                  curl_easy_setopt (eh,
     631             :                                    CURLOPT_URL,
     632             :                                    rsh->url));
     633           2 :   ctx = MAH_handle_to_context (exchange);
     634           2 :   rsh->job = GNUNET_CURL_job_add (ctx,
     635             :                           eh,
     636             :                           GNUNET_NO,
     637             :                           &handle_reserve_status_finished,
     638             :                           rsh);
     639           2 :   return rsh;
     640             : }
     641             : 
     642             : 
     643             : /**
     644             :  * Cancel a reserve status request.  This function cannot be used
     645             :  * on a request handle if a response is already served for it.
     646             :  *
     647             :  * @param rsh the reserve status request handle
     648             :  */
     649             : void
     650           2 : TALER_EXCHANGE_reserve_status_cancel (struct TALER_EXCHANGE_ReserveStatusHandle *rsh)
     651             : {
     652           2 :   if (NULL != rsh->job)
     653             :   {
     654           0 :     GNUNET_CURL_job_cancel (rsh->job);
     655           0 :     rsh->job = NULL;
     656             :   }
     657           2 :   GNUNET_free (rsh->url);
     658           2 :   GNUNET_free (rsh);
     659           2 : }
     660             : 
     661             : 
     662             : /* ********************** /reserve/withdraw ********************** */
     663             : 
     664             : /**
     665             :  * @brief A Withdraw Sign Handle
     666             :  */
     667             : struct TALER_EXCHANGE_ReserveWithdrawHandle
     668             : {
     669             : 
     670             :   /**
     671             :    * The connection to exchange this request handle will use
     672             :    */
     673             :   struct TALER_EXCHANGE_Handle *exchange;
     674             : 
     675             :   /**
     676             :    * The url for this request.
     677             :    */
     678             :   char *url;
     679             : 
     680             :   /**
     681             :    * JSON encoding of the request to POST.
     682             :    */
     683             :   char *json_enc;
     684             : 
     685             :   /**
     686             :    * Handle for the request.
     687             :    */
     688             :   struct GNUNET_CURL_Job *job;
     689             : 
     690             :   /**
     691             :    * Function to call with the result.
     692             :    */
     693             :   TALER_EXCHANGE_ReserveWithdrawResultCallback cb;
     694             : 
     695             :   /**
     696             :    * Secrets of the planchet.
     697             :    */
     698             :   struct TALER_PlanchetSecretsP ps;
     699             : 
     700             :   /**
     701             :    * Denomination key we are withdrawing.
     702             :    */
     703             :   const struct TALER_EXCHANGE_DenomPublicKey *pk;
     704             : 
     705             :   /**
     706             :    * Closure for @a cb.
     707             :    */
     708             :   void *cb_cls;
     709             : 
     710             :   /**
     711             :    * Hash of the public key of the coin we are signing.
     712             :    */
     713             :   struct GNUNET_HashCode c_hash;
     714             : 
     715             :   /**
     716             :    * Public key of the reserve we are withdrawing from.
     717             :    */
     718             :   struct TALER_ReservePublicKeyP reserve_pub;
     719             : 
     720             : };
     721             : 
     722             : 
     723             : /**
     724             :  * We got a 200 OK response for the /reserve/withdraw operation.
     725             :  * Extract the coin's signature and return it to the caller.
     726             :  * The signature we get from the exchange is for the blinded value.
     727             :  * Thus, we first must unblind it and then should verify its
     728             :  * validity against our coin's hash.
     729             :  *
     730             :  * If everything checks out, we return the unblinded signature
     731             :  * to the application via the callback.
     732             :  *
     733             :  * @param wsh operation handle
     734             :  * @param json reply from the exchange
     735             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     736             :  */
     737             : static int
     738           6 : reserve_withdraw_ok (struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh,
     739             :                      const json_t *json)
     740             : {
     741             :   struct GNUNET_CRYPTO_RsaSignature *blind_sig;
     742             :   struct TALER_FreshCoin fc;
     743           6 :   struct GNUNET_JSON_Specification spec[] = {
     744             :     GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
     745             :     GNUNET_JSON_spec_end()
     746             :   };
     747             : 
     748           6 :   if (GNUNET_OK !=
     749           6 :       GNUNET_JSON_parse (json,
     750             :                          spec,
     751             :                          NULL, NULL))
     752             :   {
     753           0 :     GNUNET_break_op (0);
     754           0 :     return GNUNET_SYSERR;
     755             :   }
     756           6 :   if (GNUNET_OK !=
     757           6 :       TALER_planchet_to_coin (&wsh->pk->key,
     758             :                               blind_sig,
     759           6 :                               &wsh->ps,
     760           6 :                               &wsh->c_hash,
     761             :                               &fc))
     762             :   {
     763           0 :     GNUNET_break_op (0);
     764           0 :     GNUNET_JSON_parse_free (spec);
     765           0 :     return GNUNET_SYSERR;
     766             :   }
     767           6 :   GNUNET_JSON_parse_free (spec);
     768             : 
     769             :   /* signature is valid, return it to the application */
     770           6 :   wsh->cb (wsh->cb_cls,
     771             :            MHD_HTTP_OK,
     772             :            TALER_EC_NONE,
     773             :            &fc.sig,
     774             :            json);
     775             :   /* make sure callback isn't called again after return */
     776           6 :   wsh->cb = NULL;
     777           6 :   GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
     778           6 :   return GNUNET_OK;
     779             : }
     780             : 
     781             : 
     782             : /**
     783             :  * We got a 403 FORBIDDEN response for the /reserve/withdraw operation.
     784             :  * Check the signatures on the withdraw transactions in the provided
     785             :  * history and that the balances add up.  We don't do anything directly
     786             :  * with the information, as the JSON will be returned to the application.
     787             :  * However, our job is ensuring that the exchange followed the protocol, and
     788             :  * this in particular means checking all of the signatures in the history.
     789             :  *
     790             :  * @param wsh operation handle
     791             :  * @param json reply from the exchange
     792             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     793             :  */
     794             : static int
     795           1 : reserve_withdraw_payment_required (struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh,
     796             :                                    const json_t *json)
     797             : {
     798             :   struct TALER_Amount balance;
     799             :   struct TALER_Amount balance_from_history;
     800             :   struct TALER_Amount requested_amount;
     801             :   json_t *history;
     802             :   size_t len;
     803           1 :   struct GNUNET_JSON_Specification spec[] = {
     804             :     TALER_JSON_spec_amount ("balance", &balance),
     805             :     GNUNET_JSON_spec_end()
     806             :   };
     807             : 
     808           1 :   if (GNUNET_OK !=
     809           1 :       GNUNET_JSON_parse (json,
     810             :                          spec,
     811             :                          NULL, NULL))
     812             :   {
     813           0 :     GNUNET_break_op (0);
     814           0 :     return GNUNET_SYSERR;
     815             :   }
     816           1 :   history = json_object_get (json,
     817             :                              "history");
     818           1 :   if (NULL == history)
     819             :   {
     820           0 :     GNUNET_break_op (0);
     821           0 :     return GNUNET_SYSERR;
     822             :   }
     823             : 
     824             :   /* go over transaction history and compute
     825             :      total incoming and outgoing amounts */
     826           1 :   len = json_array_size (history);
     827             :   {
     828             :     struct TALER_EXCHANGE_ReserveHistory *rhistory;
     829             : 
     830             :     /* Use heap allocation as "len" may be very big and thus this may
     831             :        not fit on the stack. Use "GNUNET_malloc_large" as a malicious
     832             :        exchange may theoretically try to crash us by giving a history
     833             :        that does not fit into our memory. */
     834           1 :     rhistory = GNUNET_malloc_large (sizeof (struct TALER_EXCHANGE_ReserveHistory) * len);
     835           1 :     if (NULL == rhistory)
     836             :     {
     837           0 :       GNUNET_break (0);
     838           0 :       return GNUNET_SYSERR;
     839             :     }
     840             : 
     841           1 :     if (GNUNET_OK !=
     842           2 :         parse_reserve_history (wsh->exchange,
     843             :                                history,
     844           1 :                                &wsh->reserve_pub,
     845             :                                balance.currency,
     846             :                                &balance_from_history,
     847             :                                len,
     848             :                                rhistory))
     849             :     {
     850           0 :       GNUNET_break_op (0);
     851           0 :       free_rhistory (rhistory,
     852             :                      len);
     853           0 :       return GNUNET_SYSERR;
     854             :     }
     855           1 :     free_rhistory (rhistory,
     856             :                    len);
     857             :   }
     858             : 
     859           1 :   if (0 !=
     860           1 :       TALER_amount_cmp (&balance_from_history,
     861             :                         &balance))
     862             :   {
     863             :     /* exchange cannot add up balances!? */
     864           0 :     GNUNET_break_op (0);
     865           0 :     return GNUNET_SYSERR;
     866             :   }
     867             :   /* Compute how much we expected to charge to the reserve */
     868           1 :   if (GNUNET_OK !=
     869           2 :       TALER_amount_add (&requested_amount,
     870           1 :                         &wsh->pk->value,
     871           1 :                         &wsh->pk->fee_withdraw))
     872             :   {
     873             :     /* Overflow here? Very strange, our CPU must be fried... */
     874           0 :     GNUNET_break (0);
     875           0 :     return GNUNET_SYSERR;
     876             :   }
     877             :   /* Check that funds were really insufficient */
     878           1 :   if (0 >= TALER_amount_cmp (&requested_amount,
     879             :                              &balance))
     880             :   {
     881             :     /* Requested amount is smaller or equal to reported balance,
     882             :        so this should not have failed. */
     883           0 :     GNUNET_break_op (0);
     884           0 :     return GNUNET_SYSERR;
     885             :   }
     886           1 :   return GNUNET_OK;
     887             : }
     888             : 
     889             : 
     890             : /**
     891             :  * Function called when we're done processing the
     892             :  * HTTP /reserve/withdraw request.
     893             :  *
     894             :  * @param cls the `struct TALER_EXCHANGE_ReserveWithdrawHandle`
     895             :  * @param response_code HTTP response code, 0 on error
     896             :  * @param json parsed JSON result, NULL on error
     897             :  */
     898             : static void
     899           8 : handle_reserve_withdraw_finished (void *cls,
     900             :                                   long response_code,
     901             :                                   const json_t *json)
     902             : {
     903           8 :   struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh = cls;
     904             : 
     905           8 :   wsh->job = NULL;
     906           8 :   switch (response_code)
     907             :   {
     908             :   case 0:
     909           0 :     break;
     910             :   case MHD_HTTP_OK:
     911           6 :     if (GNUNET_OK !=
     912           6 :         reserve_withdraw_ok (wsh,
     913             :                              json))
     914             :     {
     915           0 :       GNUNET_break_op (0);
     916           0 :       response_code = 0;
     917             :     }
     918           6 :     break;
     919             :   case MHD_HTTP_BAD_REQUEST:
     920             :     /* This should never happen, either us or the exchange is buggy
     921             :        (or API version conflict); just pass JSON reply to the application */
     922           0 :     break;
     923             :   case MHD_HTTP_FORBIDDEN:
     924             :     /* The exchange says that the reserve has insufficient funds;
     925             :        check the signatures in the history... */
     926           1 :     if (GNUNET_OK !=
     927           1 :         reserve_withdraw_payment_required (wsh,
     928             :                                            json))
     929             :     {
     930           0 :       GNUNET_break_op (0);
     931           0 :       response_code = 0;
     932             :     }
     933           1 :     break;
     934             :   case MHD_HTTP_UNAUTHORIZED:
     935           0 :     GNUNET_break (0);
     936             :     /* Nothing really to verify, exchange says one of the signatures is
     937             :        invalid; as we checked them, this should never happen, we
     938             :        should pass the JSON reply to the application */
     939           0 :     break;
     940             :   case MHD_HTTP_NOT_FOUND:
     941             :     /* Nothing really to verify, the exchange basically just says
     942             :        that it doesn't know this reserve.  Can happen if we
     943             :        query before the wire transfer went through.
     944             :        We should simply pass the JSON reply to the application. */
     945           1 :     break;
     946             :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     947             :     /* Server had an internal issue; we should retry, but this API
     948             :        leaves this to the application */
     949           0 :     break;
     950             :   default:
     951             :     /* unexpected response code */
     952           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     953             :                 "Unexpected response code %u\n",
     954             :                 (unsigned int) response_code);
     955           0 :     GNUNET_break (0);
     956           0 :     response_code = 0;
     957           0 :     break;
     958             :   }
     959           8 :   if (NULL != wsh->cb)
     960           2 :     wsh->cb (wsh->cb_cls,
     961             :              response_code,
     962             :              TALER_JSON_get_error_code (json),
     963             :              NULL,
     964             :              json);
     965           8 :   TALER_EXCHANGE_reserve_withdraw_cancel (wsh);
     966           8 : }
     967             : 
     968             : 
     969             : /**
     970             :  * Helper function for #TALER_EXCHANGE_reserve_withdraw2() and
     971             :  * #TALER_EXCHANGE_reserve_withdraw().
     972             :  *
     973             :  * @param exchange the exchange handle; the exchange must be ready to operate
     974             :  * @param pk kind of coin to create
     975             :  * @param reserve_sig signature from the reserve authorizing the withdrawal
     976             :  * @param reserve_pub public key of the reserve to withdraw from
     977             :  * @param ps secrets of the planchet
     978             :  *        caller must have committed this value to disk before the call (with @a pk)
     979             :  * @param pd planchet details matching @a ps
     980             :  * @param res_cb the callback to call when the final result for this request is available
     981             :  * @param res_cb_cls closure for @a res_cb
     982             :  * @return NULL
     983             :  *         if the inputs are invalid (i.e. denomination key not with this exchange).
     984             :  *         In this case, the callback is not called.
     985             :  */
     986             : struct TALER_EXCHANGE_ReserveWithdrawHandle *
     987           8 : reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
     988             :                            const struct TALER_EXCHANGE_DenomPublicKey *pk,
     989             :                            const struct TALER_ReserveSignatureP *reserve_sig,
     990             :                            const struct TALER_ReservePublicKeyP *reserve_pub,
     991             :                            const struct TALER_PlanchetSecretsP *ps,
     992             :                            const struct TALER_PlanchetDetail *pd,
     993             :                            TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb,
     994             :                            void *res_cb_cls)
     995             : {
     996             :   struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
     997             :   struct GNUNET_CURL_Context *ctx;
     998             :   json_t *withdraw_obj;
     999             :   CURL *eh;
    1000             : 
    1001           8 :   wsh = GNUNET_new (struct TALER_EXCHANGE_ReserveWithdrawHandle);
    1002           8 :   wsh->exchange = exchange;
    1003           8 :   wsh->cb = res_cb;
    1004           8 :   wsh->cb_cls = res_cb_cls;
    1005           8 :   wsh->pk = pk;
    1006           8 :   wsh->reserve_pub = *reserve_pub;
    1007           8 :   wsh->c_hash = pd->c_hash;
    1008          24 :   withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */
    1009             :                             " s:o, s:o}",/* reserve_pub and reserve_sig */
    1010           8 :                             "denom_pub", GNUNET_JSON_from_rsa_public_key (pk->key.rsa_public_key),
    1011           8 :                             "coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
    1012             :                                                               pd->coin_ev_size),
    1013             :                             "reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub),
    1014             :                             "reserve_sig", GNUNET_JSON_from_data_auto (reserve_sig));
    1015           8 :   if (NULL == withdraw_obj)
    1016             :   {
    1017           0 :     GNUNET_break (0);
    1018           0 :     return NULL;
    1019             :   }
    1020             : 
    1021           8 :   wsh->ps = *ps;
    1022           8 :   wsh->url = MAH_path_to_url (exchange, "/reserve/withdraw");
    1023             : 
    1024           8 :   eh = curl_easy_init ();
    1025           8 :   GNUNET_assert (NULL != (wsh->json_enc =
    1026             :                           json_dumps (withdraw_obj,
    1027             :                                       JSON_COMPACT)));
    1028           8 :   json_decref (withdraw_obj);
    1029           8 :   GNUNET_assert (CURLE_OK ==
    1030             :                  curl_easy_setopt (eh,
    1031             :                                    CURLOPT_URL,
    1032             :                                    wsh->url));
    1033           8 :   GNUNET_assert (CURLE_OK ==
    1034             :                  curl_easy_setopt (eh,
    1035             :                                    CURLOPT_POSTFIELDS,
    1036             :                                    wsh->json_enc));
    1037           8 :   GNUNET_assert (CURLE_OK ==
    1038             :                  curl_easy_setopt (eh,
    1039             :                                    CURLOPT_POSTFIELDSIZE,
    1040             :                                    strlen (wsh->json_enc)));
    1041           8 :   ctx = MAH_handle_to_context (exchange);
    1042           8 :   wsh->job = GNUNET_CURL_job_add (ctx,
    1043             :                           eh,
    1044             :                           GNUNET_YES,
    1045             :                           &handle_reserve_withdraw_finished,
    1046             :                           wsh);
    1047           8 :   return wsh;
    1048             : }
    1049             : 
    1050             : 
    1051             : /**
    1052             :  * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
    1053             :  * that to ensure that no money is lost in case of hardware failures,
    1054             :  * the caller must have committed (most of) the arguments to disk
    1055             :  * before calling, and be ready to repeat the request with the same
    1056             :  * arguments in case of failures.
    1057             :  *
    1058             :  * @param exchange the exchange handle; the exchange must be ready to operate
    1059             :  * @param pk kind of coin to create
    1060             :  * @param reserve_priv private key of the reserve to withdraw from
    1061             :  * @param ps secrets of the planchet
    1062             :  *        caller must have committed this value to disk before the call (with @a pk)
    1063             :  * @param res_cb the callback to call when the final result for this request is available
    1064             :  * @param res_cb_cls closure for the above callback
    1065             :  * @return handle for the operation on success, NULL on error, i.e.
    1066             :  *         if the inputs are invalid (i.e. denomination key not with this exchange).
    1067             :  *         In this case, the callback is not called.
    1068             :  */
    1069             : struct TALER_EXCHANGE_ReserveWithdrawHandle *
    1070           8 : TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange,
    1071             :                                  const struct TALER_EXCHANGE_DenomPublicKey *pk,
    1072             :                                  const struct TALER_ReservePrivateKeyP *reserve_priv,
    1073             :                                  const struct TALER_PlanchetSecretsP *ps,
    1074             :                                  TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb,
    1075             :                                  void *res_cb_cls)
    1076             : {
    1077             :   struct TALER_Amount amount_with_fee;
    1078             :   struct TALER_ReserveSignatureP reserve_sig;
    1079             :   struct TALER_WithdrawRequestPS req;
    1080             :   struct TALER_PlanchetDetail pd;
    1081             :   struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
    1082             : 
    1083           8 :   GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
    1084             :                                       &req.reserve_pub.eddsa_pub);
    1085           8 :   req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
    1086           8 :   req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
    1087           8 :   if (GNUNET_OK !=
    1088           8 :       TALER_amount_add (&amount_with_fee,
    1089             :                         &pk->fee_withdraw,
    1090             :                         &pk->value))
    1091             :   {
    1092             :     /* exchange gave us denomination keys that overflow like this!? */
    1093           0 :     GNUNET_break_op (0);
    1094           0 :     return NULL;
    1095             :   }
    1096           8 :   TALER_amount_hton (&req.amount_with_fee,
    1097             :                      &amount_with_fee);
    1098           8 :   TALER_amount_hton (&req.withdraw_fee,
    1099             :                      &pk->fee_withdraw);
    1100           8 :   if (GNUNET_OK !=
    1101           8 :       TALER_planchet_prepare (&pk->key,
    1102             :                               ps,
    1103             :                               &pd))
    1104             :   {
    1105           0 :     GNUNET_break_op (0);
    1106           0 :     return NULL;
    1107             :   }
    1108           8 :   req.h_denomination_pub = pd.denom_pub_hash;
    1109           8 :   GNUNET_CRYPTO_hash (pd.coin_ev,
    1110             :                       pd.coin_ev_size,
    1111             :                       &req.h_coin_envelope);
    1112           8 :   GNUNET_assert (GNUNET_OK ==
    1113             :                  GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
    1114             :                                            &req.purpose,
    1115             :                                            &reserve_sig.eddsa_signature));
    1116           8 :   wsh = reserve_withdraw_internal (exchange,
    1117             :                                    pk,
    1118             :                                    &reserve_sig,
    1119             :                                    &req.reserve_pub,
    1120             :                                    ps,
    1121             :                                    &pd,
    1122             :                                    res_cb,
    1123             :                                    res_cb_cls);
    1124           8 :   GNUNET_free (pd.coin_ev);
    1125           8 :   return wsh;
    1126             : }
    1127             : 
    1128             : 
    1129             : /**
    1130             :  * Withdraw a coin from the exchange using a /reserve/withdraw
    1131             :  * request.  This API is typically used by a wallet to withdraw a tip
    1132             :  * where the reserve's signature was created by the merchant already.
    1133             :  *
    1134             :  * Note that to ensure that no money is lost in case of hardware
    1135             :  * failures, the caller must have committed (most of) the arguments to
    1136             :  * disk before calling, and be ready to repeat the request with the
    1137             :  * same arguments in case of failures.
    1138             :  *
    1139             :  * @param exchange the exchange handle; the exchange must be ready to operate
    1140             :  * @param pk kind of coin to create
    1141             :  * @param reserve_sig signature from the reserve authorizing the withdrawal
    1142             :  * @param reserve_pub public key of the reserve to withdraw from
    1143             :  * @param ps secrets of the planchet
    1144             :  *        caller must have committed this value to disk before the call (with @a pk)
    1145             :  * @param res_cb the callback to call when the final result for this request is available
    1146             :  * @param res_cb_cls closure for @a res_cb
    1147             :  * @return NULL
    1148             :  *         if the inputs are invalid (i.e. denomination key not with this exchange).
    1149             :  *         In this case, the callback is not called.
    1150             :  */
    1151             : struct TALER_EXCHANGE_ReserveWithdrawHandle *
    1152           0 : TALER_EXCHANGE_reserve_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
    1153             :                                   const struct TALER_EXCHANGE_DenomPublicKey *pk,
    1154             :                                   const struct TALER_ReserveSignatureP *reserve_sig,
    1155             :                                   const struct TALER_ReservePublicKeyP *reserve_pub,
    1156             :                                   const struct TALER_PlanchetSecretsP *ps,
    1157             :                                   TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb,
    1158             :                                   void *res_cb_cls)
    1159             : {
    1160             :   struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
    1161             :   struct TALER_PlanchetDetail pd;
    1162             : 
    1163           0 :   if (GNUNET_OK !=
    1164           0 :       TALER_planchet_prepare (&pk->key,
    1165             :                               ps,
    1166             :                               &pd))
    1167             :   {
    1168           0 :     GNUNET_break_op (0);
    1169           0 :     return NULL;
    1170             :   }
    1171           0 :   wsh = reserve_withdraw_internal (exchange,
    1172             :                                    pk,
    1173             :                                    reserve_sig,
    1174             :                                    reserve_pub,
    1175             :                                    ps,
    1176             :                                    &pd,
    1177             :                                    res_cb,
    1178             :                                    res_cb_cls);
    1179           0 :   GNUNET_free (pd.coin_ev);
    1180           0 :   return wsh;
    1181             : }
    1182             : 
    1183             : 
    1184             : /**
    1185             :  * Cancel a withdraw status request.  This function cannot be used
    1186             :  * on a request handle if a response is already served for it.
    1187             :  *
    1188             :  * @param sign the withdraw sign request handle
    1189             :  */
    1190             : void
    1191           8 : TALER_EXCHANGE_reserve_withdraw_cancel (struct TALER_EXCHANGE_ReserveWithdrawHandle *sign)
    1192             : {
    1193           8 :   if (NULL != sign->job)
    1194             :   {
    1195           0 :     GNUNET_CURL_job_cancel (sign->job);
    1196           0 :     sign->job = NULL;
    1197             :   }
    1198           8 :   GNUNET_free (sign->url);
    1199           8 :   GNUNET_free (sign->json_enc);
    1200           8 :   GNUNET_free (sign);
    1201           8 : }
    1202             : 
    1203             : 
    1204             : /* end of exchange_api_reserve.c */

Generated by: LCOV version 1.13