LCOV - code coverage report
Current view: top level - lib - exchange_api_get-coins-COIN_PUB-history.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 35.5 % 437 155
Test Date: 2026-04-04 21:36:01 Functions: 47.1 % 17 8

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

Generated by: LCOV version 2.0-1