LCOV - code coverage report
Current view: top level - lib - exchange_api_coins_history.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 35.4 % 412 146
Test Date: 2025-12-28 14:06:02 Functions: 46.7 % 15 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2023 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_coins_history.c
      19              :  * @brief Implementation of the POST /coins/$COIN_PUB/history requests
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * NOTE: this is an incomplete draft, never finished!
      23              :  */
      24              : #include "taler/platform.h"
      25              : #include <jansson.h>
      26              : #include <microhttpd.h> /* just for HTTP history codes */
      27              : #include <gnunet/gnunet_util_lib.h>
      28              : #include <gnunet/gnunet_json_lib.h>
      29              : #include <gnunet/gnunet_curl_lib.h>
      30              : #include "taler/taler_exchange_service.h"
      31              : #include "taler/taler_json_lib.h"
      32              : #include "exchange_api_handle.h"
      33              : #include "taler/taler_signatures.h"
      34              : #include "exchange_api_curl_defaults.h"
      35              : 
      36              : 
      37              : /**
      38              :  * @brief A /coins/$RID/history Handle
      39              :  */
      40              : struct TALER_EXCHANGE_CoinsHistoryHandle
      41              : {
      42              : 
      43              :   /**
      44              :    * The url for this request.
      45              :    */
      46              :   char *url;
      47              : 
      48              :   /**
      49              :    * Handle for the request.
      50              :    */
      51              :   struct GNUNET_CURL_Job *job;
      52              : 
      53              :   /**
      54              :    * Context for #TEH_curl_easy_post(). Keeps the data that must
      55              :    * persist for Curl to make the upload.
      56              :    */
      57              :   struct TALER_CURL_PostContext post_ctx;
      58              : 
      59              :   /**
      60              :    * Function to call with the result.
      61              :    */
      62              :   TALER_EXCHANGE_CoinsHistoryCallback cb;
      63              : 
      64              :   /**
      65              :    * Public key of the coin we are querying.
      66              :    */
      67              :   struct TALER_CoinSpendPublicKeyP coin_pub;
      68              : 
      69              :   /**
      70              :    * Closure for @a cb.
      71              :    */
      72              :   void *cb_cls;
      73              : 
      74              : };
      75              : 
      76              : 
      77              : /**
      78              :  * Context for coin helpers.
      79              :  */
      80              : struct CoinHistoryParseContext
      81              : {
      82              : 
      83              :   /**
      84              :    * Keys of the exchange.
      85              :    */
      86              :   struct TALER_EXCHANGE_Keys *keys;
      87              : 
      88              :   /**
      89              :    * Denomination of the coin.
      90              :    */
      91              :   const struct TALER_EXCHANGE_DenomPublicKey *dk;
      92              : 
      93              :   /**
      94              :    * Our coin public key.
      95              :    */
      96              :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
      97              : 
      98              :   /**
      99              :    * Where to sum up total refunds.
     100              :    */
     101              :   struct TALER_Amount *total_in;
     102              : 
     103              :   /**
     104              :    * Total amount encountered.
     105              :    */
     106              :   struct TALER_Amount *total_out;
     107              : 
     108              : };
     109              : 
     110              : 
     111              : /**
     112              :  * Signature of functions that operate on one of
     113              :  * the coin's history entries.
     114              :  *
     115              :  * @param[in,out] pc overall context
     116              :  * @param[out] rh where to write the history entry
     117              :  * @param amount main amount of this operation
     118              :  * @param transaction JSON details for the operation
     119              :  * @return #GNUNET_SYSERR on error,
     120              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     121              :  */
     122              : typedef enum GNUNET_GenericReturnValue
     123              : (*CoinCheckHelper)(struct CoinHistoryParseContext *pc,
     124              :                    struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     125              :                    const struct TALER_Amount *amount,
     126              :                    json_t *transaction);
     127              : 
     128              : 
     129              : /**
     130              :  * Handle deposit entry in the coin's history.
     131              :  *
     132              :  * @param[in,out] pc overall context
     133              :  * @param[out] rh history entry to initialize
     134              :  * @param amount main amount of this operation
     135              :  * @param transaction JSON details for the operation
     136              :  * @return #GNUNET_SYSERR on error,
     137              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     138              :  */
     139              : static enum GNUNET_GenericReturnValue
     140            2 : help_deposit (struct CoinHistoryParseContext *pc,
     141              :               struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     142              :               const struct TALER_Amount *amount,
     143              :               json_t *transaction)
     144              : {
     145              :   struct GNUNET_JSON_Specification spec[] = {
     146            2 :     TALER_JSON_spec_amount_any ("deposit_fee",
     147              :                                 &rh->details.deposit.deposit_fee),
     148            2 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     149              :                                  &rh->details.deposit.merchant_pub),
     150            2 :     GNUNET_JSON_spec_timestamp ("timestamp",
     151              :                                 &rh->details.deposit.wallet_timestamp),
     152            2 :     GNUNET_JSON_spec_mark_optional (
     153              :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     154              :                                   &rh->details.deposit.refund_deadline),
     155              :       NULL),
     156            2 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     157              :                                  &rh->details.deposit.h_contract_terms),
     158            2 :     GNUNET_JSON_spec_fixed_auto ("h_wire",
     159              :                                  &rh->details.deposit.h_wire),
     160            2 :     GNUNET_JSON_spec_mark_optional (
     161            2 :       GNUNET_JSON_spec_fixed_auto ("h_policy",
     162              :                                    &rh->details.deposit.h_policy),
     163              :       &rh->details.deposit.no_h_policy),
     164            2 :     GNUNET_JSON_spec_mark_optional (
     165            2 :       GNUNET_JSON_spec_fixed_auto ("wallet_data_hash",
     166              :                                    &rh->details.deposit.wallet_data_hash),
     167              :       &rh->details.deposit.no_wallet_data_hash),
     168            2 :     GNUNET_JSON_spec_mark_optional (
     169            2 :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     170              :                                    &rh->details.deposit.hac),
     171              :       &rh->details.deposit.no_hac),
     172            2 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     173              :                                  &rh->details.deposit.sig),
     174            2 :     GNUNET_JSON_spec_end ()
     175              :   };
     176              : 
     177            2 :   rh->details.deposit.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS;
     178            2 :   if (GNUNET_OK !=
     179            2 :       GNUNET_JSON_parse (transaction,
     180              :                          spec,
     181              :                          NULL, NULL))
     182              :   {
     183            0 :     GNUNET_break_op (0);
     184            0 :     return GNUNET_SYSERR;
     185              :   }
     186            2 :   if (GNUNET_OK !=
     187            6 :       TALER_wallet_deposit_verify (
     188              :         amount,
     189            2 :         &rh->details.deposit.deposit_fee,
     190            2 :         &rh->details.deposit.h_wire,
     191            2 :         &rh->details.deposit.h_contract_terms,
     192            2 :         rh->details.deposit.no_wallet_data_hash
     193              :         ? NULL
     194              :         : &rh->details.deposit.wallet_data_hash,
     195            2 :         rh->details.deposit.no_hac
     196              :         ? NULL
     197              :         : &rh->details.deposit.hac,
     198            2 :         rh->details.deposit.no_h_policy
     199              :         ? NULL
     200              :         : &rh->details.deposit.h_policy,
     201            2 :         &pc->dk->h_key,
     202              :         rh->details.deposit.wallet_timestamp,
     203            2 :         &rh->details.deposit.merchant_pub,
     204              :         rh->details.deposit.refund_deadline,
     205              :         pc->coin_pub,
     206            2 :         &rh->details.deposit.sig))
     207              :   {
     208            0 :     GNUNET_break_op (0);
     209            0 :     return GNUNET_SYSERR;
     210              :   }
     211              :   /* check that deposit fee matches our expectations from /keys! */
     212            2 :   if ( (GNUNET_YES !=
     213            2 :         TALER_amount_cmp_currency (&rh->details.deposit.deposit_fee,
     214            4 :                                    &pc->dk->fees.deposit)) ||
     215              :        (0 !=
     216            2 :         TALER_amount_cmp (&rh->details.deposit.deposit_fee,
     217            2 :                           &pc->dk->fees.deposit)) )
     218              :   {
     219            0 :     GNUNET_break_op (0);
     220            0 :     return GNUNET_SYSERR;
     221              :   }
     222            2 :   return GNUNET_YES;
     223              : }
     224              : 
     225              : 
     226              : /**
     227              :  * Handle melt entry in the coin's history.
     228              :  *
     229              :  * @param[in,out] pc overall context
     230              :  * @param[out] rh history entry to initialize
     231              :  * @param amount main amount of this operation
     232              :  * @param transaction JSON details for the operation
     233              :  * @return #GNUNET_SYSERR on error,
     234              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     235              :  */
     236              : static enum GNUNET_GenericReturnValue
     237            0 : help_melt (struct CoinHistoryParseContext *pc,
     238              :            struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     239              :            const struct TALER_Amount *amount,
     240              :            json_t *transaction)
     241              : {
     242              :   struct GNUNET_JSON_Specification spec[] = {
     243            0 :     TALER_JSON_spec_amount_any ("melt_fee",
     244              :                                 &rh->details.melt.melt_fee),
     245            0 :     GNUNET_JSON_spec_fixed_auto ("rc",
     246              :                                  &rh->details.melt.rc),
     247              :     // FIXME: also return refresh_seed?
     248              :     // FIXME: also return blinding_seed?
     249            0 :     GNUNET_JSON_spec_mark_optional (
     250            0 :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     251              :                                    &rh->details.melt.h_age_commitment),
     252              :       &rh->details.melt.no_hac),
     253            0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     254              :                                  &rh->details.melt.sig),
     255            0 :     GNUNET_JSON_spec_end ()
     256              :   };
     257              : 
     258            0 :   if (GNUNET_OK !=
     259            0 :       GNUNET_JSON_parse (transaction,
     260              :                          spec,
     261              :                          NULL, NULL))
     262              :   {
     263            0 :     GNUNET_break_op (0);
     264            0 :     return GNUNET_SYSERR;
     265              :   }
     266              : 
     267              :   /* check that melt fee matches our expectations from /keys! */
     268            0 :   if ( (GNUNET_YES !=
     269            0 :         TALER_amount_cmp_currency (&rh->details.melt.melt_fee,
     270            0 :                                    &pc->dk->fees.refresh)) ||
     271              :        (0 !=
     272            0 :         TALER_amount_cmp (&rh->details.melt.melt_fee,
     273            0 :                           &pc->dk->fees.refresh)) )
     274              :   {
     275            0 :     GNUNET_break_op (0);
     276            0 :     return GNUNET_SYSERR;
     277              :   }
     278            0 :   if (GNUNET_OK !=
     279            0 :       TALER_wallet_melt_verify (
     280              :         amount,
     281            0 :         &rh->details.melt.melt_fee,
     282            0 :         &rh->details.melt.rc,
     283            0 :         &pc->dk->h_key,
     284            0 :         rh->details.melt.no_hac
     285              :         ? NULL
     286              :         : &rh->details.melt.h_age_commitment,
     287              :         pc->coin_pub,
     288            0 :         &rh->details.melt.sig))
     289              :   {
     290            0 :     GNUNET_break_op (0);
     291            0 :     return GNUNET_SYSERR;
     292              :   }
     293            0 :   return GNUNET_YES;
     294              : }
     295              : 
     296              : 
     297              : /**
     298              :  * Handle refund entry in the coin's history.
     299              :  *
     300              :  * @param[in,out] pc overall context
     301              :  * @param[out] rh history entry to initialize
     302              :  * @param amount main amount of this operation
     303              :  * @param transaction JSON details for the operation
     304              :  * @return #GNUNET_SYSERR on error,
     305              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     306              :  */
     307              : static enum GNUNET_GenericReturnValue
     308            0 : help_refund (struct CoinHistoryParseContext *pc,
     309              :              struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     310              :              const struct TALER_Amount *amount,
     311              :              json_t *transaction)
     312              : {
     313              :   struct GNUNET_JSON_Specification spec[] = {
     314            0 :     TALER_JSON_spec_amount_any ("refund_fee",
     315              :                                 &rh->details.refund.refund_fee),
     316            0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     317              :                                  &rh->details.refund.h_contract_terms),
     318            0 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     319              :                                  &rh->details.refund.merchant_pub),
     320            0 :     GNUNET_JSON_spec_uint64 ("rtransaction_id",
     321              :                              &rh->details.refund.rtransaction_id),
     322            0 :     GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     323              :                                  &rh->details.refund.sig),
     324            0 :     GNUNET_JSON_spec_end ()
     325              :   };
     326              : 
     327            0 :   if (GNUNET_OK !=
     328            0 :       GNUNET_JSON_parse (transaction,
     329              :                          spec,
     330              :                          NULL, NULL))
     331              :   {
     332            0 :     GNUNET_break_op (0);
     333            0 :     return GNUNET_SYSERR;
     334              :   }
     335            0 :   if (0 >
     336            0 :       TALER_amount_add (&rh->details.refund.sig_amount,
     337            0 :                         &rh->details.refund.refund_fee,
     338              :                         amount))
     339              :   {
     340            0 :     GNUNET_break_op (0);
     341            0 :     return GNUNET_SYSERR;
     342              :   }
     343            0 :   if (GNUNET_OK !=
     344            0 :       TALER_merchant_refund_verify (pc->coin_pub,
     345            0 :                                     &rh->details.refund.h_contract_terms,
     346              :                                     rh->details.refund.rtransaction_id,
     347            0 :                                     &rh->details.refund.sig_amount,
     348            0 :                                     &rh->details.refund.merchant_pub,
     349            0 :                                     &rh->details.refund.sig))
     350              :   {
     351            0 :     GNUNET_break_op (0);
     352            0 :     return GNUNET_SYSERR;
     353              :   }
     354              :   /* NOTE: theoretically, we could also check that the given
     355              :      merchant_pub and h_contract_terms appear in the
     356              :      history under deposits.  However, there is really no benefit
     357              :      for the exchange to lie here, so not checking is probably OK
     358              :      (an auditor ought to check, though). Then again, we similarly
     359              :      had no reason to check the merchant's signature (other than a
     360              :      well-formendess check). */
     361              : 
     362              :   /* check that refund fee matches our expectations from /keys! */
     363            0 :   if ( (GNUNET_YES !=
     364            0 :         TALER_amount_cmp_currency (&rh->details.refund.refund_fee,
     365            0 :                                    &pc->dk->fees.refund)) ||
     366              :        (0 !=
     367            0 :         TALER_amount_cmp (&rh->details.refund.refund_fee,
     368            0 :                           &pc->dk->fees.refund)) )
     369              :   {
     370            0 :     GNUNET_break_op (0);
     371            0 :     return GNUNET_SYSERR;
     372              :   }
     373            0 :   return GNUNET_NO;
     374              : }
     375              : 
     376              : 
     377              : /**
     378              :  * Handle recoup entry in the coin's history.
     379              :  *
     380              :  * @param[in,out] pc overall context
     381              :  * @param[out] rh history entry to initialize
     382              :  * @param amount main amount of this operation
     383              :  * @param transaction JSON details for the operation
     384              :  * @return #GNUNET_SYSERR on error,
     385              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     386              :  */
     387              : static enum GNUNET_GenericReturnValue
     388            0 : help_recoup (struct CoinHistoryParseContext *pc,
     389              :              struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     390              :              const struct TALER_Amount *amount,
     391              :              json_t *transaction)
     392              : {
     393              :   struct GNUNET_JSON_Specification spec[] = {
     394            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     395              :                                  &rh->details.recoup.exchange_sig),
     396            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     397              :                                  &rh->details.recoup.exchange_pub),
     398            0 :     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     399              :                                  &rh->details.recoup.reserve_pub),
     400            0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     401              :                                  &rh->details.recoup.coin_sig),
     402            0 :     GNUNET_JSON_spec_fixed_auto ("coin_blind",
     403              :                                  &rh->details.recoup.coin_bks),
     404            0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     405              :                                 &rh->details.recoup.timestamp),
     406            0 :     GNUNET_JSON_spec_end ()
     407              :   };
     408              : 
     409            0 :   if (GNUNET_OK !=
     410            0 :       GNUNET_JSON_parse (transaction,
     411              :                          spec,
     412              :                          NULL, NULL))
     413              :   {
     414            0 :     GNUNET_break_op (0);
     415            0 :     return GNUNET_SYSERR;
     416              :   }
     417            0 :   if (GNUNET_OK !=
     418            0 :       TALER_exchange_online_confirm_recoup_verify (
     419              :         rh->details.recoup.timestamp,
     420              :         amount,
     421              :         pc->coin_pub,
     422            0 :         &rh->details.recoup.reserve_pub,
     423            0 :         &rh->details.recoup.exchange_pub,
     424            0 :         &rh->details.recoup.exchange_sig))
     425              :   {
     426            0 :     GNUNET_break_op (0);
     427            0 :     return GNUNET_SYSERR;
     428              :   }
     429            0 :   if (GNUNET_OK !=
     430            0 :       TALER_wallet_recoup_verify (&pc->dk->h_key,
     431            0 :                                   &rh->details.recoup.coin_bks,
     432              :                                   pc->coin_pub,
     433            0 :                                   &rh->details.recoup.coin_sig))
     434              :   {
     435            0 :     GNUNET_break_op (0);
     436            0 :     return GNUNET_SYSERR;
     437              :   }
     438            0 :   return GNUNET_YES;
     439              : }
     440              : 
     441              : 
     442              : /**
     443              :  * Handle recoup-refresh entry in the coin's history.
     444              :  * This is the coin that was subjected to a recoup,
     445              :  * the value being credited to the old coin.
     446              :  *
     447              :  * @param[in,out] pc overall context
     448              :  * @param[out] rh history entry to initialize
     449              :  * @param amount main amount of this operation
     450              :  * @param transaction JSON details for the operation
     451              :  * @return #GNUNET_SYSERR on error,
     452              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     453              :  */
     454              : static enum GNUNET_GenericReturnValue
     455            0 : help_recoup_refresh (struct CoinHistoryParseContext *pc,
     456              :                      struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     457              :                      const struct TALER_Amount *amount,
     458              :                      json_t *transaction)
     459              : {
     460              :   struct GNUNET_JSON_Specification spec[] = {
     461            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     462              :                                  &rh->details.recoup_refresh.exchange_sig),
     463            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     464              :                                  &rh->details.recoup_refresh.exchange_pub),
     465            0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     466              :                                  &rh->details.recoup_refresh.coin_sig),
     467            0 :     GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
     468              :                                  &rh->details.recoup_refresh.old_coin_pub),
     469            0 :     GNUNET_JSON_spec_fixed_auto ("coin_blind",
     470              :                                  &rh->details.recoup_refresh.coin_bks),
     471            0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     472              :                                 &rh->details.recoup_refresh.timestamp),
     473            0 :     GNUNET_JSON_spec_end ()
     474              :   };
     475              : 
     476            0 :   if (GNUNET_OK !=
     477            0 :       GNUNET_JSON_parse (transaction,
     478              :                          spec,
     479              :                          NULL, NULL))
     480              :   {
     481            0 :     GNUNET_break_op (0);
     482            0 :     return GNUNET_SYSERR;
     483              :   }
     484            0 :   if (GNUNET_OK !=
     485            0 :       TALER_exchange_online_confirm_recoup_refresh_verify (
     486              :         rh->details.recoup_refresh.timestamp,
     487              :         amount,
     488              :         pc->coin_pub,
     489            0 :         &rh->details.recoup_refresh.old_coin_pub,
     490            0 :         &rh->details.recoup_refresh.exchange_pub,
     491            0 :         &rh->details.recoup_refresh.exchange_sig))
     492              :   {
     493            0 :     GNUNET_break_op (0);
     494            0 :     return GNUNET_SYSERR;
     495              :   }
     496            0 :   if (GNUNET_OK !=
     497            0 :       TALER_wallet_recoup_verify (&pc->dk->h_key,
     498            0 :                                   &rh->details.recoup_refresh.coin_bks,
     499              :                                   pc->coin_pub,
     500            0 :                                   &rh->details.recoup_refresh.coin_sig))
     501              :   {
     502            0 :     GNUNET_break_op (0);
     503            0 :     return GNUNET_SYSERR;
     504              :   }
     505            0 :   return GNUNET_YES;
     506              : }
     507              : 
     508              : 
     509              : /**
     510              :  * Handle old coin recoup entry in the coin's history.
     511              :  * This is the coin that was credited in a recoup,
     512              :  * the value being credited to the this coin.
     513              :  *
     514              :  * @param[in,out] pc overall context
     515              :  * @param[out] rh history entry to initialize
     516              :  * @param amount main amount of this operation
     517              :  * @param transaction JSON details for the operation
     518              :  * @return #GNUNET_SYSERR on error,
     519              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     520              :  */
     521              : static enum GNUNET_GenericReturnValue
     522            0 : help_old_coin_recoup (struct CoinHistoryParseContext *pc,
     523              :                       struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     524              :                       const struct TALER_Amount *amount,
     525              :                       json_t *transaction)
     526              : {
     527              :   struct GNUNET_JSON_Specification spec[] = {
     528            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     529              :                                  &rh->details.old_coin_recoup.exchange_sig),
     530            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     531              :                                  &rh->details.old_coin_recoup.exchange_pub),
     532            0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     533              :                                  &rh->details.old_coin_recoup.new_coin_pub),
     534            0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     535              :                                 &rh->details.old_coin_recoup.timestamp),
     536            0 :     GNUNET_JSON_spec_end ()
     537              :   };
     538              : 
     539            0 :   if (GNUNET_OK !=
     540            0 :       GNUNET_JSON_parse (transaction,
     541              :                          spec,
     542              :                          NULL, NULL))
     543              :   {
     544            0 :     GNUNET_break_op (0);
     545            0 :     return GNUNET_SYSERR;
     546              :   }
     547            0 :   if (GNUNET_OK !=
     548            0 :       TALER_exchange_online_confirm_recoup_refresh_verify (
     549              :         rh->details.old_coin_recoup.timestamp,
     550              :         amount,
     551            0 :         &rh->details.old_coin_recoup.new_coin_pub,
     552              :         pc->coin_pub,
     553            0 :         &rh->details.old_coin_recoup.exchange_pub,
     554            0 :         &rh->details.old_coin_recoup.exchange_sig))
     555              :   {
     556            0 :     GNUNET_break_op (0);
     557            0 :     return GNUNET_SYSERR;
     558              :   }
     559            0 :   return GNUNET_NO;
     560              : }
     561              : 
     562              : 
     563              : /**
     564              :  * Handle purse deposit entry in the coin's history.
     565              :  *
     566              :  * @param[in,out] pc overall context
     567              :  * @param[out] rh history entry to initialize
     568              :  * @param amount main amount of this operation
     569              :  * @param transaction JSON details for the operation
     570              :  * @return #GNUNET_SYSERR on error,
     571              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     572              :  */
     573              : static enum GNUNET_GenericReturnValue
     574            3 : help_purse_deposit (struct CoinHistoryParseContext *pc,
     575              :                     struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     576              :                     const struct TALER_Amount *amount,
     577              :                     json_t *transaction)
     578              : {
     579              :   struct GNUNET_JSON_Specification spec[] = {
     580            3 :     TALER_JSON_spec_web_url ("exchange_base_url",
     581              :                              &rh->details.purse_deposit.exchange_base_url),
     582            3 :     GNUNET_JSON_spec_mark_optional (
     583            3 :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     584              :                                    &rh->details.purse_deposit.phac),
     585              :       NULL),
     586              :     // FIXME: return deposit_fee?
     587            3 :     GNUNET_JSON_spec_fixed_auto ("purse_pub",
     588              :                                  &rh->details.purse_deposit.purse_pub),
     589            3 :     GNUNET_JSON_spec_bool ("refunded",
     590              :                            &rh->details.purse_deposit.refunded),
     591            3 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     592              :                                  &rh->details.purse_deposit.coin_sig),
     593            3 :     GNUNET_JSON_spec_end ()
     594              :   };
     595              : 
     596            3 :   if (GNUNET_OK !=
     597            3 :       GNUNET_JSON_parse (transaction,
     598              :                          spec,
     599              :                          NULL, NULL))
     600              :   {
     601            0 :     GNUNET_break_op (0);
     602            0 :     return GNUNET_SYSERR;
     603              :   }
     604            3 :   if (GNUNET_OK !=
     605            3 :       TALER_wallet_purse_deposit_verify (
     606              :         rh->details.purse_deposit.exchange_base_url,
     607            3 :         &rh->details.purse_deposit.purse_pub,
     608              :         amount,
     609            3 :         &pc->dk->h_key,
     610            3 :         &rh->details.purse_deposit.phac,
     611              :         pc->coin_pub,
     612            3 :         &rh->details.purse_deposit.coin_sig))
     613              :   {
     614            0 :     GNUNET_break_op (0);
     615            0 :     return GNUNET_SYSERR;
     616              :   }
     617            3 :   if (rh->details.purse_deposit.refunded)
     618              :   {
     619              :     /* We wave the deposit fee. */
     620            0 :     if (0 >
     621            0 :         TALER_amount_add (pc->total_in,
     622            0 :                           pc->total_in,
     623            0 :                           &pc->dk->fees.deposit))
     624              :     {
     625              :       /* overflow in refund history? inconceivable! Bad exchange! */
     626            0 :       GNUNET_break_op (0);
     627            0 :       return GNUNET_SYSERR;
     628              :     }
     629              :   }
     630            3 :   return GNUNET_YES;
     631              : }
     632              : 
     633              : 
     634              : /**
     635              :  * Handle purse refund entry in the coin's history.
     636              :  *
     637              :  * @param[in,out] pc overall context
     638              :  * @param[out] rh history entry to initialize
     639              :  * @param amount main amount of this operation
     640              :  * @param transaction JSON details for the operation
     641              :  * @return #GNUNET_SYSERR on error,
     642              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     643              :  */
     644              : static enum GNUNET_GenericReturnValue
     645            0 : help_purse_refund (struct CoinHistoryParseContext *pc,
     646              :                    struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     647              :                    const struct TALER_Amount *amount,
     648              :                    json_t *transaction)
     649              : {
     650              :   struct GNUNET_JSON_Specification spec[] = {
     651            0 :     TALER_JSON_spec_amount_any ("refund_fee",
     652              :                                 &rh->details.purse_refund.refund_fee),
     653            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     654              :                                  &rh->details.purse_refund.exchange_sig),
     655            0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     656              :                                  &rh->details.purse_refund.exchange_pub),
     657            0 :     GNUNET_JSON_spec_fixed_auto ("purse_pub",
     658              :                                  &rh->details.purse_refund.purse_pub),
     659            0 :     GNUNET_JSON_spec_end ()
     660              :   };
     661              : 
     662            0 :   if (GNUNET_OK !=
     663            0 :       GNUNET_JSON_parse (transaction,
     664              :                          spec,
     665              :                          NULL, NULL))
     666              :   {
     667            0 :     GNUNET_break_op (0);
     668            0 :     return GNUNET_SYSERR;
     669              :   }
     670            0 :   if (GNUNET_OK !=
     671            0 :       TALER_exchange_online_purse_refund_verify (
     672              :         amount,
     673            0 :         &rh->details.purse_refund.refund_fee,
     674              :         pc->coin_pub,
     675            0 :         &rh->details.purse_refund.purse_pub,
     676            0 :         &rh->details.purse_refund.exchange_pub,
     677            0 :         &rh->details.purse_refund.exchange_sig))
     678              :   {
     679            0 :     GNUNET_break_op (0);
     680            0 :     return GNUNET_SYSERR;
     681              :   }
     682            0 :   if ( (GNUNET_YES !=
     683            0 :         TALER_amount_cmp_currency (&rh->details.purse_refund.refund_fee,
     684            0 :                                    &pc->dk->fees.refund)) ||
     685              :        (0 !=
     686            0 :         TALER_amount_cmp (&rh->details.purse_refund.refund_fee,
     687            0 :                           &pc->dk->fees.refund)) )
     688              :   {
     689            0 :     GNUNET_break_op (0);
     690            0 :     return GNUNET_SYSERR;
     691              :   }
     692            0 :   return GNUNET_NO;
     693              : }
     694              : 
     695              : 
     696              : /**
     697              :  * Handle reserve deposit entry in the coin's history.
     698              :  *
     699              :  * @param[in,out] pc overall context
     700              :  * @param[out] rh history entry to initialize
     701              :  * @param amount main amount of this operation
     702              :  * @param transaction JSON details for the operation
     703              :  * @return #GNUNET_SYSERR on error,
     704              :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     705              :  */
     706              : static enum GNUNET_GenericReturnValue
     707            0 : help_reserve_open_deposit (struct CoinHistoryParseContext *pc,
     708              :                            struct TALER_EXCHANGE_CoinHistoryEntry *rh,
     709              :                            const struct TALER_Amount *amount,
     710              :                            json_t *transaction)
     711              : {
     712              :   struct GNUNET_JSON_Specification spec[] = {
     713            0 :     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     714              :                                  &rh->details.reserve_open_deposit.reserve_sig),
     715            0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     716              :                                  &rh->details.reserve_open_deposit.coin_sig),
     717            0 :     GNUNET_JSON_spec_end ()
     718              :   };
     719              : 
     720            0 :   if (GNUNET_OK !=
     721            0 :       GNUNET_JSON_parse (transaction,
     722              :                          spec,
     723              :                          NULL, NULL))
     724              :   {
     725            0 :     GNUNET_break_op (0);
     726            0 :     return GNUNET_SYSERR;
     727              :   }
     728            0 :   if (GNUNET_OK !=
     729            0 :       TALER_wallet_reserve_open_deposit_verify (
     730              :         amount,
     731            0 :         &rh->details.reserve_open_deposit.reserve_sig,
     732              :         pc->coin_pub,
     733            0 :         &rh->details.reserve_open_deposit.coin_sig))
     734              :   {
     735            0 :     GNUNET_break_op (0);
     736            0 :     return GNUNET_SYSERR;
     737              :   }
     738            0 :   return GNUNET_YES;
     739              : }
     740              : 
     741              : 
     742              : enum GNUNET_GenericReturnValue
     743            4 : TALER_EXCHANGE_parse_coin_history (
     744              :   const struct TALER_EXCHANGE_Keys *keys,
     745              :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
     746              :   const json_t *history,
     747              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     748              :   struct TALER_Amount *total_in,
     749              :   struct TALER_Amount *total_out,
     750              :   unsigned int rlen,
     751              :   struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen])
     752            4 : {
     753              :   const struct
     754              :   {
     755              :     const char *type;
     756              :     CoinCheckHelper helper;
     757              :     enum TALER_EXCHANGE_CoinTransactionType ctt;
     758            4 :   } map[] = {
     759              :     { "DEPOSIT",
     760              :       &help_deposit,
     761              :       TALER_EXCHANGE_CTT_DEPOSIT },
     762              :     { "MELT",
     763              :       &help_melt,
     764              :       TALER_EXCHANGE_CTT_MELT },
     765              :     { "REFUND",
     766              :       &help_refund,
     767              :       TALER_EXCHANGE_CTT_REFUND },
     768              :     { "RECOUP",
     769              :       &help_recoup,
     770              :       TALER_EXCHANGE_CTT_RECOUP },
     771              :     { "RECOUP-REFRESH",
     772              :       &help_recoup_refresh,
     773              :       TALER_EXCHANGE_CTT_RECOUP_REFRESH },
     774              :     { "OLD-COIN-RECOUP",
     775              :       &help_old_coin_recoup,
     776              :       TALER_EXCHANGE_CTT_OLD_COIN_RECOUP },
     777              :     { "PURSE-DEPOSIT",
     778              :       &help_purse_deposit,
     779              :       TALER_EXCHANGE_CTT_PURSE_DEPOSIT },
     780              :     { "PURSE-REFUND",
     781              :       &help_purse_refund,
     782              :       TALER_EXCHANGE_CTT_PURSE_REFUND },
     783              :     { "RESERVE-OPEN-DEPOSIT",
     784              :       &help_reserve_open_deposit,
     785              :       TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT },
     786              :     { NULL, NULL, TALER_EXCHANGE_CTT_NONE }
     787              :   };
     788            4 :   struct CoinHistoryParseContext pc = {
     789              :     .dk = dk,
     790              :     .coin_pub = coin_pub,
     791              :     .total_out = total_out,
     792              :     .total_in = total_in
     793              :   };
     794              :   size_t len;
     795              : 
     796            4 :   if (NULL == history)
     797              :   {
     798            0 :     GNUNET_break_op (0);
     799            0 :     return GNUNET_SYSERR;
     800              :   }
     801            4 :   len = json_array_size (history);
     802            4 :   if (0 == len)
     803              :   {
     804            0 :     GNUNET_break_op (0);
     805            0 :     return GNUNET_SYSERR;
     806              :   }
     807            4 :   *total_in = dk->value;
     808            4 :   GNUNET_assert (GNUNET_OK ==
     809              :                  TALER_amount_set_zero (total_in->currency,
     810              :                                         total_out));
     811            9 :   for (size_t off = 0; off<len; off++)
     812              :   {
     813            5 :     struct TALER_EXCHANGE_CoinHistoryEntry *rh = &rhistory[off];
     814            5 :     json_t *transaction = json_array_get (history,
     815              :                                           off);
     816              :     enum GNUNET_GenericReturnValue add;
     817              :     const char *type;
     818              :     struct GNUNET_JSON_Specification spec_glob[] = {
     819            5 :       TALER_JSON_spec_amount_any ("amount",
     820              :                                   &rh->amount),
     821            5 :       GNUNET_JSON_spec_string ("type",
     822              :                                &type),
     823            5 :       GNUNET_JSON_spec_uint64 ("history_offset",
     824              :                                &rh->history_offset),
     825            5 :       GNUNET_JSON_spec_end ()
     826              :     };
     827              : 
     828            5 :     if (GNUNET_OK !=
     829            5 :         GNUNET_JSON_parse (transaction,
     830              :                            spec_glob,
     831              :                            NULL, NULL))
     832              :     {
     833            0 :       GNUNET_break_op (0);
     834            0 :       return GNUNET_SYSERR;
     835              :     }
     836            5 :     if (GNUNET_YES !=
     837            5 :         TALER_amount_cmp_currency (&rh->amount,
     838              :                                    total_in))
     839              :     {
     840            0 :       GNUNET_break_op (0);
     841            0 :       return GNUNET_SYSERR;
     842              :     }
     843            5 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     844              :                 "Operation of type %s with amount %s\n",
     845              :                 type,
     846              :                 TALER_amount2s (&rh->amount));
     847            5 :     add = GNUNET_SYSERR;
     848           23 :     for (unsigned int i = 0; NULL != map[i].type; i++)
     849              :     {
     850           23 :       if (0 == strcasecmp (type,
     851           23 :                            map[i].type))
     852              :       {
     853            5 :         rh->type = map[i].ctt;
     854            5 :         add = map[i].helper (&pc,
     855              :                              rh,
     856            5 :                              &rh->amount,
     857              :                              transaction);
     858            5 :         break;
     859              :       }
     860              :     }
     861            5 :     switch (add)
     862              :     {
     863            0 :     case GNUNET_SYSERR:
     864              :       /* entry type not supported, new version on server? */
     865            0 :       rh->type = TALER_EXCHANGE_CTT_NONE;
     866            0 :       GNUNET_break_op (0);
     867            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     868              :                   "Unexpected type `%s' in response\n",
     869              :                   type);
     870            0 :       return GNUNET_SYSERR;
     871            5 :     case GNUNET_YES:
     872              :       /* This amount should be debited from the coin */
     873            5 :       if (0 >
     874            5 :           TALER_amount_add (total_out,
     875              :                             total_out,
     876            5 :                             &rh->amount))
     877              :       {
     878              :         /* overflow in history already!? inconceivable! Bad exchange! */
     879            0 :         GNUNET_break_op (0);
     880            0 :         return GNUNET_SYSERR;
     881              :       }
     882            5 :       break;
     883            0 :     case GNUNET_NO:
     884              :       /* This amount should be credited to the coin. */
     885            0 :       if (0 >
     886            0 :           TALER_amount_add (total_in,
     887              :                             total_in,
     888            0 :                             &rh->amount))
     889              :       {
     890              :         /* overflow in refund history? inconceivable! Bad exchange! */
     891            0 :         GNUNET_break_op (0);
     892            0 :         return GNUNET_SYSERR;
     893              :       }
     894            0 :       break;
     895              :     } /* end of switch(add) */
     896              :   }
     897            4 :   return GNUNET_OK;
     898              : }
     899              : 
     900              : 
     901              : /**
     902              :  * We received an #MHD_HTTP_OK history code. Handle the JSON
     903              :  * response.
     904              :  *
     905              :  * @param rsh handle of the request
     906              :  * @param j JSON response
     907              :  * @return #GNUNET_OK on success
     908              :  */
     909              : static enum GNUNET_GenericReturnValue
     910            4 : handle_coins_history_ok (struct TALER_EXCHANGE_CoinsHistoryHandle *rsh,
     911              :                          const json_t *j)
     912              : {
     913            4 :   struct TALER_EXCHANGE_CoinHistory rs = {
     914              :     .hr.reply = j,
     915              :     .hr.http_status = MHD_HTTP_OK
     916              :   };
     917              :   struct GNUNET_JSON_Specification spec[] = {
     918            4 :     TALER_JSON_spec_amount_any ("balance",
     919              :                                 &rs.details.ok.balance),
     920            4 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     921              :                                  &rs.details.ok.h_denom_pub),
     922            4 :     GNUNET_JSON_spec_array_const ("history",
     923              :                                   &rs.details.ok.history),
     924            4 :     GNUNET_JSON_spec_end ()
     925              :   };
     926              : 
     927            4 :   if (GNUNET_OK !=
     928            4 :       GNUNET_JSON_parse (j,
     929              :                          spec,
     930              :                          NULL,
     931              :                          NULL))
     932              :   {
     933            0 :     GNUNET_break_op (0);
     934            0 :     return GNUNET_SYSERR;
     935              :   }
     936            4 :   if (NULL != rsh->cb)
     937              :   {
     938            4 :     rsh->cb (rsh->cb_cls,
     939              :              &rs);
     940            4 :     rsh->cb = NULL;
     941              :   }
     942            4 :   GNUNET_JSON_parse_free (spec);
     943            4 :   return GNUNET_OK;
     944              : }
     945              : 
     946              : 
     947              : /**
     948              :  * Function called when we're done processing the
     949              :  * HTTP /coins/$RID/history request.
     950              :  *
     951              :  * @param cls the `struct TALER_EXCHANGE_CoinsHistoryHandle`
     952              :  * @param response_code HTTP response code, 0 on error
     953              :  * @param response parsed JSON result, NULL on error
     954              :  */
     955              : static void
     956            4 : handle_coins_history_finished (void *cls,
     957              :                                long response_code,
     958              :                                const void *response)
     959              : {
     960            4 :   struct TALER_EXCHANGE_CoinsHistoryHandle *rsh = cls;
     961            4 :   const json_t *j = response;
     962            4 :   struct TALER_EXCHANGE_CoinHistory rs = {
     963              :     .hr.reply = j,
     964            4 :     .hr.http_status = (unsigned int) response_code
     965              :   };
     966              : 
     967            4 :   rsh->job = NULL;
     968            4 :   switch (response_code)
     969              :   {
     970            0 :   case 0:
     971            0 :     rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     972            0 :     break;
     973            4 :   case MHD_HTTP_OK:
     974            4 :     if (GNUNET_OK !=
     975            4 :         handle_coins_history_ok (rsh,
     976              :                                  j))
     977              :     {
     978            0 :       rs.hr.http_status = 0;
     979            0 :       rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     980              :     }
     981            4 :     break;
     982            0 :   case MHD_HTTP_BAD_REQUEST:
     983              :     /* This should never happen, either us or the exchange is buggy
     984              :        (or API version conflict); just pass JSON reply to the application */
     985            0 :     GNUNET_break (0);
     986            0 :     rs.hr.ec = TALER_JSON_get_error_code (j);
     987            0 :     rs.hr.hint = TALER_JSON_get_error_hint (j);
     988            0 :     break;
     989            0 :   case MHD_HTTP_FORBIDDEN:
     990              :     /* This should never happen, either us or the exchange is buggy
     991              :        (or API version conflict); just pass JSON reply to the application */
     992            0 :     GNUNET_break (0);
     993            0 :     rs.hr.ec = TALER_JSON_get_error_code (j);
     994            0 :     rs.hr.hint = TALER_JSON_get_error_hint (j);
     995            0 :     break;
     996            0 :   case MHD_HTTP_NOT_FOUND:
     997              :     /* Nothing really to verify, this should never
     998              :        happen, we should pass the JSON reply to the application */
     999            0 :     rs.hr.ec = TALER_JSON_get_error_code (j);
    1000            0 :     rs.hr.hint = TALER_JSON_get_error_hint (j);
    1001            0 :     break;
    1002            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    1003              :     /* Server had an internal issue; we should retry, but this API
    1004              :        leaves this to the application */
    1005            0 :     rs.hr.ec = TALER_JSON_get_error_code (j);
    1006            0 :     rs.hr.hint = TALER_JSON_get_error_hint (j);
    1007            0 :     break;
    1008            0 :   default:
    1009              :     /* unexpected response code */
    1010            0 :     GNUNET_break_op (0);
    1011            0 :     rs.hr.ec = TALER_JSON_get_error_code (j);
    1012            0 :     rs.hr.hint = TALER_JSON_get_error_hint (j);
    1013            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1014              :                 "Unexpected response code %u/%d for coins history\n",
    1015              :                 (unsigned int) response_code,
    1016              :                 (int) rs.hr.ec);
    1017            0 :     break;
    1018              :   }
    1019            4 :   if (NULL != rsh->cb)
    1020              :   {
    1021            0 :     rsh->cb (rsh->cb_cls,
    1022              :              &rs);
    1023            0 :     rsh->cb = NULL;
    1024              :   }
    1025            4 :   TALER_EXCHANGE_coins_history_cancel (rsh);
    1026            4 : }
    1027              : 
    1028              : 
    1029              : struct TALER_EXCHANGE_CoinsHistoryHandle *
    1030            4 : TALER_EXCHANGE_coins_history (
    1031              :   struct GNUNET_CURL_Context *ctx,
    1032              :   const char *url,
    1033              :   const struct TALER_CoinSpendPrivateKeyP *coin_priv,
    1034              :   uint64_t start_off,
    1035              :   TALER_EXCHANGE_CoinsHistoryCallback cb,
    1036              :   void *cb_cls)
    1037              : {
    1038              :   struct TALER_EXCHANGE_CoinsHistoryHandle *rsh;
    1039              :   CURL *eh;
    1040              :   char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64];
    1041              :   struct curl_slist *job_headers;
    1042              : 
    1043            4 :   rsh = GNUNET_new (struct TALER_EXCHANGE_CoinsHistoryHandle);
    1044            4 :   rsh->cb = cb;
    1045            4 :   rsh->cb_cls = cb_cls;
    1046            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
    1047              :                                       &rsh->coin_pub.eddsa_pub);
    1048              :   {
    1049              :     char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
    1050              :     char *end;
    1051              : 
    1052            4 :     end = GNUNET_STRINGS_data_to_string (
    1053            4 :       &rsh->coin_pub,
    1054              :       sizeof (rsh->coin_pub),
    1055              :       pub_str,
    1056              :       sizeof (pub_str));
    1057            4 :     *end = '\0';
    1058            4 :     if (0 != start_off)
    1059            0 :       GNUNET_snprintf (arg_str,
    1060              :                        sizeof (arg_str),
    1061              :                        "coins/%s/history?start=%llu",
    1062              :                        pub_str,
    1063              :                        (unsigned long long) start_off);
    1064              :     else
    1065            4 :       GNUNET_snprintf (arg_str,
    1066              :                        sizeof (arg_str),
    1067              :                        "coins/%s/history",
    1068              :                        pub_str);
    1069              :   }
    1070            4 :   rsh->url = TALER_url_join (url,
    1071              :                              arg_str,
    1072              :                              NULL);
    1073            4 :   if (NULL == rsh->url)
    1074              :   {
    1075            0 :     GNUNET_free (rsh);
    1076            0 :     return NULL;
    1077              :   }
    1078            4 :   eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url);
    1079            4 :   if (NULL == eh)
    1080              :   {
    1081            0 :     GNUNET_break (0);
    1082            0 :     GNUNET_free (rsh->url);
    1083            0 :     GNUNET_free (rsh);
    1084            0 :     return NULL;
    1085              :   }
    1086              : 
    1087              :   {
    1088              :     struct TALER_CoinSpendSignatureP coin_sig;
    1089              :     char *sig_hdr;
    1090              :     char *hdr;
    1091              : 
    1092            4 :     TALER_wallet_coin_history_sign (start_off,
    1093              :                                     coin_priv,
    1094              :                                     &coin_sig);
    1095              : 
    1096            4 :     sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
    1097              :       &coin_sig,
    1098              :       sizeof (coin_sig));
    1099            4 :     GNUNET_asprintf (&hdr,
    1100              :                      "%s: %s",
    1101              :                      TALER_COIN_HISTORY_SIGNATURE_HEADER,
    1102              :                      sig_hdr);
    1103            4 :     GNUNET_free (sig_hdr);
    1104            4 :     job_headers = curl_slist_append (NULL,
    1105              :                                      hdr);
    1106            4 :     GNUNET_free (hdr);
    1107            4 :     if (NULL == job_headers)
    1108              :     {
    1109            0 :       GNUNET_break (0);
    1110            0 :       curl_easy_cleanup (eh);
    1111            0 :       return NULL;
    1112              :     }
    1113              :   }
    1114              : 
    1115            4 :   rsh->job = GNUNET_CURL_job_add2 (ctx,
    1116              :                                    eh,
    1117              :                                    job_headers,
    1118              :                                    &handle_coins_history_finished,
    1119              :                                    rsh);
    1120            4 :   curl_slist_free_all (job_headers);
    1121            4 :   return rsh;
    1122              : }
    1123              : 
    1124              : 
    1125              : void
    1126            4 : TALER_EXCHANGE_coins_history_cancel (
    1127              :   struct TALER_EXCHANGE_CoinsHistoryHandle *rsh)
    1128              : {
    1129            4 :   if (NULL != rsh->job)
    1130              :   {
    1131            0 :     GNUNET_CURL_job_cancel (rsh->job);
    1132            0 :     rsh->job = NULL;
    1133              :   }
    1134            4 :   TALER_curl_easy_post_finished (&rsh->post_ctx);
    1135            4 :   GNUNET_free (rsh->url);
    1136            4 :   GNUNET_free (rsh);
    1137            4 : }
    1138              : 
    1139              : 
    1140              : /**
    1141              :  * Verify that @a coin_sig does NOT appear in the @a history of a coin's
    1142              :  * transactions and thus whatever transaction is authorized by @a coin_sig is
    1143              :  * a conflict with @a proof.
    1144              :  *
    1145              :  * @param history coin history to check
    1146              :  * @param coin_sig signature that must not be in @a history
    1147              :  * @return #GNUNET_OK if @a coin_sig is not in @a history
    1148              :  */
    1149              : enum GNUNET_GenericReturnValue
    1150            0 : TALER_EXCHANGE_check_coin_signature_conflict (
    1151              :   const json_t *history,
    1152              :   const struct TALER_CoinSpendSignatureP *coin_sig)
    1153              : {
    1154              :   size_t off;
    1155              :   json_t *entry;
    1156              : 
    1157            0 :   json_array_foreach (history, off, entry)
    1158              :   {
    1159              :     struct TALER_CoinSpendSignatureP cs;
    1160              :     struct GNUNET_JSON_Specification spec[] = {
    1161            0 :       GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1162              :                                    &cs),
    1163            0 :       GNUNET_JSON_spec_end ()
    1164              :     };
    1165              : 
    1166            0 :     if (GNUNET_OK !=
    1167            0 :         GNUNET_JSON_parse (entry,
    1168              :                            spec,
    1169              :                            NULL, NULL))
    1170            0 :       continue; /* entry without coin signature */
    1171            0 :     if (0 ==
    1172            0 :         GNUNET_memcmp (&cs,
    1173              :                        coin_sig))
    1174              :     {
    1175            0 :       GNUNET_break_op (0);
    1176            0 :       return GNUNET_SYSERR;
    1177              :     }
    1178              :   }
    1179            0 :   return GNUNET_OK;
    1180              : }
    1181              : 
    1182              : 
    1183              : #if FIXME_IMPLEMENT /* #9422 */
    1184              : /**
    1185              :  * FIXME-Oec-#9422: we need some specific routines that show
    1186              :  * that certain coin operations are indeed in conflict,
    1187              :  * for example that the coin is of a different denomination
    1188              :  * or different age restrictions.
    1189              :  * This relates to unimplemented error handling for
    1190              :  * coins in the exchange!
    1191              :  *
    1192              :  * Check that the provided @a proof indeeds indicates
    1193              :  * a conflict for @a coin_pub.
    1194              :  *
    1195              :  * @param keys exchange keys
    1196              :  * @param proof provided conflict proof
    1197              :  * @param dk denomination of @a coin_pub that the client
    1198              :  *           used
    1199              :  * @param coin_pub public key of the coin
    1200              :  * @param required balance required on the coin for the operation
    1201              :  * @return #GNUNET_OK if @a proof holds
    1202              :  */
    1203              : // FIXME-#9422: should be properly defined and implemented!
    1204              : enum GNUNET_GenericReturnValue
    1205              : TALER_EXCHANGE_check_coin_conflict_ (
    1206              :   const struct TALER_EXCHANGE_Keys *keys,
    1207              :   const json_t *proof,
    1208              :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
    1209              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1210              :   const struct TALER_Amount *required)
    1211              : {
    1212              :   enum TALER_ErrorCode ec;
    1213              : 
    1214              :   ec = TALER_JSON_get_error_code (proof);
    1215              :   switch (ec)
    1216              :   {
    1217              :   case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
    1218              :     /* Nothing to check anymore here, proof needs to be
    1219              :        checked in the GET /coins/$COIN_PUB handler */
    1220              :     break;
    1221              :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
    1222              :     // FIXME-#9422: write check!
    1223              :     break;
    1224              :   default:
    1225              :     GNUNET_break_op (0);
    1226              :     return GNUNET_SYSERR;
    1227              :   }
    1228              :   return GNUNET_OK;
    1229              : }
    1230              : 
    1231              : 
    1232              : #endif
    1233              : 
    1234              : 
    1235              : /* end of exchange_api_coins_history.c */
        

Generated by: LCOV version 2.0-1