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

Generated by: LCOV version 2.0-1