LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_coins_get.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 61 206 29.6 %
Date: 2025-06-22 12:09:43 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2023 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Affero 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 Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-exchange-httpd_coins_get.c
      18             :  * @brief Handle GET /coins/$COIN_PUB/history requests
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "taler/platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include <jansson.h>
      24             : #include "taler/taler_mhd_lib.h"
      25             : #include "taler/taler_json_lib.h"
      26             : #include "taler/taler_dbevents.h"
      27             : #include "taler-exchange-httpd_keys.h"
      28             : #include "taler-exchange-httpd_coins_get.h"
      29             : #include "taler-exchange-httpd_responses.h"
      30             : 
      31             : 
      32             : /**
      33             :  * Add the headers we want to set for every response.
      34             :  *
      35             :  * @param cls the key state to use
      36             :  * @param[in,out] response the response to modify
      37             :  */
      38             : static void
      39           4 : add_response_headers (void *cls,
      40             :                       struct MHD_Response *response)
      41             : {
      42             :   (void) cls;
      43           4 :   GNUNET_break (MHD_YES ==
      44             :                 MHD_add_response_header (response,
      45             :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
      46             :                                          "no-cache"));
      47           4 : }
      48             : 
      49             : 
      50             : /**
      51             :  * Compile the transaction history of a coin into a JSON object.
      52             :  *
      53             :  * @param coin_pub public key of the coin
      54             :  * @param tl transaction history to JSON-ify
      55             :  * @return json representation of the @a rh, NULL on error
      56             :  */
      57             : static json_t *
      58           4 : compile_transaction_history (
      59             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
      60             :   const struct TALER_EXCHANGEDB_TransactionList *tl)
      61             : {
      62             :   json_t *history;
      63             : 
      64           4 :   history = json_array ();
      65           4 :   if (NULL == history)
      66             :   {
      67           0 :     GNUNET_break (0); /* out of memory!? */
      68           0 :     return NULL;
      69             :   }
      70           4 :   for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl;
      71           9 :        NULL != pos;
      72           5 :        pos = pos->next)
      73             :   {
      74           5 :     switch (pos->type)
      75             :     {
      76           2 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
      77             :       {
      78           2 :         const struct TALER_EXCHANGEDB_DepositListEntry *deposit =
      79             :           pos->details.deposit;
      80             :         struct TALER_MerchantWireHashP h_wire;
      81             : 
      82           2 :         TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
      83             :                                             &deposit->wire_salt,
      84             :                                             &h_wire);
      85             : #if ENABLE_SANITY_CHECKS
      86             :         /* internal sanity check before we hand out a bogus sig... */
      87           2 :         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
      88           2 :         if (GNUNET_OK !=
      89           4 :             TALER_wallet_deposit_verify (
      90             :               &deposit->amount_with_fee,
      91             :               &deposit->deposit_fee,
      92             :               &h_wire,
      93             :               &deposit->h_contract_terms,
      94           2 :               deposit->no_wallet_data_hash
      95             :               ? NULL
      96             :               : &deposit->wallet_data_hash,
      97           2 :               deposit->no_age_commitment
      98             :               ? NULL
      99             :               : &deposit->h_age_commitment,
     100             :               &deposit->h_policy,
     101             :               &deposit->h_denom_pub,
     102             :               deposit->timestamp,
     103             :               &deposit->merchant_pub,
     104             :               deposit->refund_deadline,
     105             :               coin_pub,
     106             :               &deposit->csig))
     107             :         {
     108           0 :           GNUNET_break (0);
     109           0 :           json_decref (history);
     110           0 :           return NULL;
     111             :         }
     112             : #endif
     113           2 :         if (0 !=
     114           2 :             json_array_append_new (
     115             :               history,
     116           2 :               GNUNET_JSON_PACK (
     117             :                 GNUNET_JSON_pack_string ("type",
     118             :                                          "DEPOSIT"),
     119             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     120             :                                          pos->coin_history_id),
     121             :                 TALER_JSON_pack_amount ("amount",
     122             :                                         &deposit->amount_with_fee),
     123             :                 TALER_JSON_pack_amount ("deposit_fee",
     124             :                                         &deposit->deposit_fee),
     125             :                 GNUNET_JSON_pack_data_auto ("merchant_pub",
     126             :                                             &deposit->merchant_pub),
     127             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     128             :                                             deposit->timestamp),
     129             :                 GNUNET_JSON_pack_allow_null (
     130             :                   GNUNET_JSON_pack_timestamp ("refund_deadline",
     131             :                                               deposit->refund_deadline)),
     132             :                 GNUNET_JSON_pack_data_auto ("h_contract_terms",
     133             :                                             &deposit->h_contract_terms),
     134             :                 GNUNET_JSON_pack_data_auto ("h_wire",
     135             :                                             &h_wire),
     136             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     137             :                                             &deposit->h_denom_pub),
     138             :                 GNUNET_JSON_pack_allow_null (
     139             :                   deposit->has_policy
     140             :                   ? GNUNET_JSON_pack_data_auto ("h_policy",
     141             :                                                 &deposit->h_policy)
     142             :                   : GNUNET_JSON_pack_string (
     143             :                     "h_policy",
     144             :                     NULL)),
     145             :                 GNUNET_JSON_pack_allow_null (
     146             :                   deposit->no_wallet_data_hash
     147             :                   ? GNUNET_JSON_pack_string (
     148             :                     "wallet_data_hash",
     149             :                     NULL)
     150             :                   : GNUNET_JSON_pack_data_auto ("wallet_data_hash",
     151             :                                                 &deposit->wallet_data_hash)),
     152             :                 GNUNET_JSON_pack_allow_null (
     153             :                   deposit->no_age_commitment
     154             :                   ? GNUNET_JSON_pack_string (
     155             :                     "h_age_commitment",
     156             :                     NULL)
     157             :                   : GNUNET_JSON_pack_data_auto ("h_age_commitment",
     158             :                                                 &deposit->h_age_commitment)),
     159             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     160             :                                             &deposit->csig))))
     161             :         {
     162           0 :           GNUNET_break (0);
     163           0 :           json_decref (history);
     164           0 :           return NULL;
     165             :         }
     166           2 :         break;
     167             :       }
     168           0 :     case TALER_EXCHANGEDB_TT_MELT:
     169             :       {
     170           0 :         const struct TALER_EXCHANGEDB_MeltListEntry *melt =
     171             :           pos->details.melt;
     172             :         const struct TALER_AgeCommitmentHashP *phac;
     173             :         const struct TALER_BlindingMasterSeedP *pbs;
     174             : 
     175             : #if ENABLE_SANITY_CHECKS
     176           0 :         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     177           0 :         if (GNUNET_OK !=
     178           0 :             TALER_wallet_melt_verify (
     179             :               &melt->amount_with_fee,
     180             :               &melt->melt_fee,
     181             :               &melt->rc,
     182             :               &melt->h_denom_pub,
     183             :               &melt->h_age_commitment,
     184             :               coin_pub,
     185             :               &melt->coin_sig))
     186             :         {
     187           0 :           GNUNET_break (0);
     188           0 :           json_decref (history);
     189           0 :           return NULL;
     190             :         }
     191             : #endif
     192           0 :         phac = (melt->no_age_commitment)
     193             :                 ? NULL
     194           0 :                 :  &melt->h_age_commitment;
     195           0 :         pbs = melt->no_blinding_seed
     196             :                 ? NULL
     197           0 :                 : &melt->blinding_seed;
     198           0 :         if (0 !=
     199           0 :             json_array_append_new (
     200             :               history,
     201           0 :               GNUNET_JSON_PACK (
     202             :                 GNUNET_JSON_pack_string ("type",
     203             :                                          "MELT"),
     204             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     205             :                                          pos->coin_history_id),
     206             :                 TALER_JSON_pack_amount ("amount",
     207             :                                         &melt->amount_with_fee),
     208             :                 TALER_JSON_pack_amount ("melt_fee",
     209             :                                         &melt->melt_fee),
     210             :                 GNUNET_JSON_pack_data_auto ("rc",
     211             :                                             &melt->rc),
     212             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     213             :                                             &melt->h_denom_pub),
     214             :                 GNUNET_JSON_pack_data_auto ("refresh_seed",
     215             :                                             &melt->refresh_seed),
     216             :                 GNUNET_JSON_pack_allow_null (
     217             :                   GNUNET_JSON_pack_data_auto ("blinding_seed",
     218             :                                               pbs)),
     219             :                 GNUNET_JSON_pack_allow_null (
     220             :                   GNUNET_JSON_pack_data_auto ("h_age_commitment",
     221             :                                               phac)),
     222             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     223             :                                             &melt->coin_sig))))
     224             :         {
     225           0 :           GNUNET_break (0);
     226           0 :           json_decref (history);
     227           0 :           return NULL;
     228             :         }
     229             :       }
     230           0 :       break;
     231           0 :     case TALER_EXCHANGEDB_TT_REFUND:
     232             :       {
     233           0 :         const struct TALER_EXCHANGEDB_RefundListEntry *refund =
     234             :           pos->details.refund;
     235             :         struct TALER_Amount value;
     236             : 
     237             : #if ENABLE_SANITY_CHECKS
     238           0 :         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     239           0 :         if (GNUNET_OK !=
     240           0 :             TALER_merchant_refund_verify (
     241             :               coin_pub,
     242             :               &refund->h_contract_terms,
     243           0 :               refund->rtransaction_id,
     244             :               &refund->refund_amount,
     245             :               &refund->merchant_pub,
     246             :               &refund->merchant_sig))
     247             :         {
     248           0 :           GNUNET_break (0);
     249           0 :           json_decref (history);
     250           0 :           return NULL;
     251             :         }
     252             : #endif
     253           0 :         if (0 >
     254           0 :             TALER_amount_subtract (&value,
     255             :                                    &refund->refund_amount,
     256             :                                    &refund->refund_fee))
     257             :         {
     258           0 :           GNUNET_break (0);
     259           0 :           json_decref (history);
     260           0 :           return NULL;
     261             :         }
     262           0 :         if (0 !=
     263           0 :             json_array_append_new (
     264             :               history,
     265           0 :               GNUNET_JSON_PACK (
     266             :                 GNUNET_JSON_pack_string ("type",
     267             :                                          "REFUND"),
     268             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     269             :                                          pos->coin_history_id),
     270             :                 TALER_JSON_pack_amount ("amount",
     271             :                                         &value),
     272             :                 TALER_JSON_pack_amount ("refund_fee",
     273             :                                         &refund->refund_fee),
     274             :                 GNUNET_JSON_pack_data_auto ("h_contract_terms",
     275             :                                             &refund->h_contract_terms),
     276             :                 GNUNET_JSON_pack_data_auto ("merchant_pub",
     277             :                                             &refund->merchant_pub),
     278             :                 GNUNET_JSON_pack_uint64 ("rtransaction_id",
     279             :                                          refund->rtransaction_id),
     280             :                 GNUNET_JSON_pack_data_auto ("merchant_sig",
     281             :                                             &refund->merchant_sig))))
     282             :         {
     283           0 :           GNUNET_break (0);
     284           0 :           json_decref (history);
     285           0 :           return NULL;
     286             :         }
     287             :       }
     288           0 :       break;
     289           0 :     case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
     290             :       {
     291           0 :         struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
     292             :           pos->details.old_coin_recoup;
     293             :         struct TALER_ExchangePublicKeyP epub;
     294             :         struct TALER_ExchangeSignatureP esig;
     295             : 
     296           0 :         if (TALER_EC_NONE !=
     297           0 :             TALER_exchange_online_confirm_recoup_refresh_sign (
     298             :               &TEH_keys_exchange_sign_,
     299             :               pr->timestamp,
     300           0 :               &pr->value,
     301           0 :               &pr->coin.coin_pub,
     302           0 :               &pr->old_coin_pub,
     303             :               &epub,
     304             :               &esig))
     305             :         {
     306           0 :           GNUNET_break (0);
     307           0 :           json_decref (history);
     308           0 :           return NULL;
     309             :         }
     310             :         /* NOTE: we could also provide coin_pub's coin_sig, denomination key hash and
     311             :            the denomination key's RSA signature over coin_pub, but as the
     312             :            wallet should really already have this information (and cannot
     313             :            check or do anything with it anyway if it doesn't), it seems
     314             :            strictly unnecessary. */
     315           0 :         if (0 !=
     316           0 :             json_array_append_new (
     317             :               history,
     318           0 :               GNUNET_JSON_PACK (
     319             :                 GNUNET_JSON_pack_string ("type",
     320             :                                          "OLD-COIN-RECOUP"),
     321             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     322             :                                          pos->coin_history_id),
     323             :                 TALER_JSON_pack_amount ("amount",
     324             :                                         &pr->value),
     325             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     326             :                                             &esig),
     327             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     328             :                                             &epub),
     329             :                 GNUNET_JSON_pack_data_auto ("coin_pub",
     330             :                                             &pr->coin.coin_pub),
     331             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     332             :                                             pr->timestamp))))
     333             :         {
     334           0 :           GNUNET_break (0);
     335           0 :           json_decref (history);
     336           0 :           return NULL;
     337             :         }
     338           0 :         break;
     339             :       }
     340           0 :     case TALER_EXCHANGEDB_TT_RECOUP:
     341             :       {
     342           0 :         const struct TALER_EXCHANGEDB_RecoupListEntry *recoup =
     343             :           pos->details.recoup;
     344             :         struct TALER_ExchangePublicKeyP epub;
     345             :         struct TALER_ExchangeSignatureP esig;
     346             : 
     347           0 :         if (TALER_EC_NONE !=
     348           0 :             TALER_exchange_online_confirm_recoup_sign (
     349             :               &TEH_keys_exchange_sign_,
     350             :               recoup->timestamp,
     351             :               &recoup->value,
     352             :               coin_pub,
     353             :               &recoup->reserve_pub,
     354             :               &epub,
     355             :               &esig))
     356             :         {
     357           0 :           GNUNET_break (0);
     358           0 :           json_decref (history);
     359           0 :           return NULL;
     360             :         }
     361           0 :         if (0 !=
     362           0 :             json_array_append_new (
     363             :               history,
     364           0 :               GNUNET_JSON_PACK (
     365             :                 GNUNET_JSON_pack_string ("type",
     366             :                                          "RECOUP"),
     367             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     368             :                                          pos->coin_history_id),
     369             :                 TALER_JSON_pack_amount ("amount",
     370             :                                         &recoup->value),
     371             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     372             :                                             &esig),
     373             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     374             :                                             &epub),
     375             :                 GNUNET_JSON_pack_data_auto ("reserve_pub",
     376             :                                             &recoup->reserve_pub),
     377             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     378             :                                             &recoup->coin_sig),
     379             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     380             :                                             &recoup->h_denom_pub),
     381             :                 GNUNET_JSON_pack_data_auto ("coin_blind",
     382             :                                             &recoup->coin_blind),
     383             :                 // FIXME-9828: spec says we should have h_commitment?
     384             :                 // FIXME-9828: spec says we should have coin_index?
     385             :                 GNUNET_JSON_pack_data_auto ("reserve_pub",
     386             :                                             &recoup->reserve_pub),
     387             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     388             :                                             recoup->timestamp))))
     389             :         {
     390           0 :           GNUNET_break (0);
     391           0 :           json_decref (history);
     392           0 :           return NULL;
     393             :         }
     394             :       }
     395           0 :       break;
     396           0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
     397             :       {
     398           0 :         struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
     399             :           pos->details.recoup_refresh;
     400             :         struct TALER_ExchangePublicKeyP epub;
     401             :         struct TALER_ExchangeSignatureP esig;
     402             : 
     403           0 :         if (TALER_EC_NONE !=
     404           0 :             TALER_exchange_online_confirm_recoup_refresh_sign (
     405             :               &TEH_keys_exchange_sign_,
     406             :               pr->timestamp,
     407           0 :               &pr->value,
     408             :               coin_pub,
     409           0 :               &pr->old_coin_pub,
     410             :               &epub,
     411             :               &esig))
     412             :         {
     413           0 :           GNUNET_break (0);
     414           0 :           json_decref (history);
     415           0 :           return NULL;
     416             :         }
     417             :         /* NOTE: we could also provide coin_pub's coin_sig, denomination key
     418             :            hash and the denomination key's RSA signature over coin_pub, but as
     419             :            the wallet should really already have this information (and cannot
     420             :            check or do anything with it anyway if it doesn't), it seems
     421             :            strictly unnecessary. */
     422           0 :         if (0 !=
     423           0 :             json_array_append_new (
     424             :               history,
     425           0 :               GNUNET_JSON_PACK (
     426             :                 GNUNET_JSON_pack_string ("type",
     427             :                                          "RECOUP-REFRESH"),
     428             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     429             :                                          pos->coin_history_id),
     430             :                 TALER_JSON_pack_amount ("amount",
     431             :                                         &pr->value),
     432             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     433             :                                             &esig),
     434             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     435             :                                             &epub),
     436             :                 GNUNET_JSON_pack_data_auto ("old_coin_pub",
     437             :                                             &pr->old_coin_pub),
     438             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     439             :                                             &pr->coin_sig),
     440             :                 // FIXME-#9828: spec says to return h_denom_pub
     441             :                 GNUNET_JSON_pack_data_auto ("coin_blind",
     442             :                                             &pr->coin_blind),
     443             :                 // FIXME-#9828: spec says to return h_commitment
     444             :                 // FIXME-#9828: spec says to return coin_index
     445             :                 // FIXME-#9828: spec says to return new_coin_blinding_secret
     446             :                 // FIXME-#9828: spec says to return new_coin_ev
     447             :                 GNUNET_JSON_pack_timestamp ("timestamp",
     448             :                                             pr->timestamp))))
     449             :         {
     450           0 :           GNUNET_break (0);
     451           0 :           json_decref (history);
     452           0 :           return NULL;
     453             :         }
     454           0 :         break;
     455             :       }
     456             : 
     457           3 :     case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
     458             :       {
     459           3 :         struct TALER_EXCHANGEDB_PurseDepositListEntry *pd
     460             :           = pos->details.purse_deposit;
     461           3 :         const struct TALER_AgeCommitmentHashP *phac = NULL;
     462             : 
     463           3 :         if (! pd->no_age_commitment)
     464           0 :           phac = &pd->h_age_commitment;
     465           3 :         if (0 !=
     466           3 :             json_array_append_new (
     467             :               history,
     468           3 :               GNUNET_JSON_PACK (
     469             :                 GNUNET_JSON_pack_string ("type",
     470             :                                          "PURSE-DEPOSIT"),
     471             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     472             :                                          pos->coin_history_id),
     473             :                 TALER_JSON_pack_amount ("amount",
     474             :                                         &pd->amount),
     475             :                 GNUNET_JSON_pack_string ("exchange_base_url",
     476             :                                          NULL == pd->exchange_base_url
     477             :                                          ? TEH_base_url
     478             :                                          : pd->exchange_base_url),
     479             :                 GNUNET_JSON_pack_allow_null (
     480             :                   GNUNET_JSON_pack_data_auto ("h_age_commitment",
     481             :                                               phac)),
     482             :                 TALER_JSON_pack_amount ("deposit_fee",
     483             :                                         &pd->deposit_fee),
     484             :                 GNUNET_JSON_pack_data_auto ("purse_pub",
     485             :                                             &pd->purse_pub),
     486             :                 GNUNET_JSON_pack_bool ("refunded",
     487             :                                        pd->refunded),
     488             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     489             :                                             &pd->coin_sig),
     490             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     491             :                                             &pd->h_denom_pub))))
     492             :         {
     493           0 :           GNUNET_break (0);
     494           0 :           json_decref (history);
     495           0 :           return NULL;
     496             :         }
     497           3 :         break;
     498             :       }
     499             : 
     500           0 :     case TALER_EXCHANGEDB_TT_PURSE_REFUND:
     501             :       {
     502           0 :         const struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund =
     503             :           pos->details.purse_refund;
     504             :         struct TALER_Amount value;
     505             :         enum TALER_ErrorCode ec;
     506             :         struct TALER_ExchangePublicKeyP epub;
     507             :         struct TALER_ExchangeSignatureP esig;
     508             : 
     509           0 :         if (0 >
     510           0 :             TALER_amount_subtract (&value,
     511             :                                    &prefund->refund_amount,
     512             :                                    &prefund->refund_fee))
     513             :         {
     514           0 :           GNUNET_break (0);
     515           0 :           json_decref (history);
     516           0 :           return NULL;
     517             :         }
     518           0 :         ec = TALER_exchange_online_purse_refund_sign (
     519             :           &TEH_keys_exchange_sign_,
     520             :           &value,
     521             :           &prefund->refund_fee,
     522             :           coin_pub,
     523             :           &prefund->purse_pub,
     524             :           &epub,
     525             :           &esig);
     526           0 :         if (TALER_EC_NONE != ec)
     527             :         {
     528           0 :           GNUNET_break (0);
     529           0 :           json_decref (history);
     530           0 :           return NULL;
     531             :         }
     532           0 :         if (0 !=
     533           0 :             json_array_append_new (
     534             :               history,
     535           0 :               GNUNET_JSON_PACK (
     536             :                 GNUNET_JSON_pack_string ("type",
     537             :                                          "PURSE-REFUND"),
     538             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     539             :                                          pos->coin_history_id),
     540             :                 TALER_JSON_pack_amount ("amount",
     541             :                                         &value),
     542             :                 TALER_JSON_pack_amount ("refund_fee",
     543             :                                         &prefund->refund_fee),
     544             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     545             :                                             &esig),
     546             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     547             :                                             &epub),
     548             :                 GNUNET_JSON_pack_data_auto ("purse_pub",
     549             :                                             &prefund->purse_pub))))
     550             :         {
     551           0 :           GNUNET_break (0);
     552           0 :           json_decref (history);
     553           0 :           return NULL;
     554             :         }
     555             :       }
     556           0 :       break;
     557             : 
     558           0 :     case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
     559             :       {
     560           0 :         struct TALER_EXCHANGEDB_ReserveOpenListEntry *role
     561             :           = pos->details.reserve_open;
     562             : 
     563           0 :         if (0 !=
     564           0 :             json_array_append_new (
     565             :               history,
     566           0 :               GNUNET_JSON_PACK (
     567             :                 GNUNET_JSON_pack_string ("type",
     568             :                                          "RESERVE-OPEN-DEPOSIT"),
     569             :                 GNUNET_JSON_pack_uint64 ("history_offset",
     570             :                                          pos->coin_history_id),
     571             :                 TALER_JSON_pack_amount ("coin_contribution",
     572             :                                         &role->coin_contribution),
     573             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     574             :                                             &role->reserve_sig),
     575             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     576             :                                             &role->coin_sig))))
     577             :         {
     578           0 :           GNUNET_break (0);
     579           0 :           json_decref (history);
     580           0 :           return NULL;
     581             :         }
     582           0 :         break;
     583             :       }
     584             :     }
     585             :   }
     586           4 :   return history;
     587             : }
     588             : 
     589             : 
     590             : MHD_RESULT
     591           4 : TEH_handler_coins_get (struct TEH_RequestContext *rc,
     592             :                        const struct TALER_CoinSpendPublicKeyP *coin_pub)
     593             : {
     594           4 :   struct TALER_EXCHANGEDB_TransactionList *tl = NULL;
     595           4 :   uint64_t start_off = 0;
     596             :   uint64_t etag_in;
     597             :   uint64_t etag_out;
     598             :   char etagp[24];
     599             :   struct MHD_Response *resp;
     600             :   unsigned int http_status;
     601             :   struct TALER_DenominationHashP h_denom_pub;
     602             :   struct TALER_Amount balance;
     603             : 
     604           4 :   TALER_MHD_parse_request_number (rc->connection,
     605             :                                   "start",
     606             :                                   &start_off);
     607             :   /* Check signature */
     608             :   {
     609             :     struct TALER_CoinSpendSignatureP coin_sig;
     610           4 :     bool required = true;
     611             : 
     612           4 :     TALER_MHD_parse_request_header_auto (rc->connection,
     613             :                                          TALER_COIN_HISTORY_SIGNATURE_HEADER,
     614             :                                          &coin_sig,
     615             :                                          required);
     616           4 :     if (GNUNET_OK !=
     617           4 :         TALER_wallet_coin_history_verify (start_off,
     618             :                                           coin_pub,
     619             :                                           &coin_sig))
     620             :     {
     621           0 :       GNUNET_break_op (0);
     622           0 :       return TALER_MHD_reply_with_error (rc->connection,
     623             :                                          MHD_HTTP_FORBIDDEN,
     624             :                                          TALER_EC_EXCHANGE_COIN_HISTORY_BAD_SIGNATURE,
     625             :                                          NULL);
     626             :     }
     627             :   }
     628             : 
     629             :   /* Get etag */
     630             :   {
     631             :     const char *etags;
     632             : 
     633           4 :     etags = MHD_lookup_connection_value (rc->connection,
     634             :                                          MHD_HEADER_KIND,
     635             :                                          MHD_HTTP_HEADER_IF_NONE_MATCH);
     636           4 :     if (NULL != etags)
     637             :     {
     638             :       char dummy;
     639             :       unsigned long long ev;
     640             : 
     641           0 :       if (1 != sscanf (etags,
     642             :                        "\"%llu\"%c",
     643             :                        &ev,
     644             :                        &dummy))
     645             :       {
     646           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     647             :                     "Client send malformed `If-None-Match' header `%s'\n",
     648             :                     etags);
     649           0 :         etag_in = start_off;
     650             :       }
     651             :       else
     652             :       {
     653           0 :         etag_in = (uint64_t) ev;
     654             :       }
     655             :     }
     656             :     else
     657             :     {
     658           4 :       etag_in = start_off;
     659             :     }
     660             :   }
     661             : 
     662             :   /* Get history from DB between etag and now */
     663             :   {
     664             :     enum GNUNET_DB_QueryStatus qs;
     665             : 
     666           4 :     qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
     667             :                                             true,
     668             :                                             coin_pub,
     669             :                                             start_off,
     670             :                                             etag_in,
     671             :                                             &etag_out,
     672             :                                             &balance,
     673             :                                             &h_denom_pub,
     674             :                                             &tl);
     675           4 :     switch (qs)
     676             :     {
     677           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     678           0 :       GNUNET_break (0);
     679           0 :       return TALER_MHD_reply_with_error (rc->connection,
     680             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     681             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     682             :                                          "get_coin_history");
     683           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     684           0 :       return TALER_MHD_reply_with_error (rc->connection,
     685             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     686             :                                          TALER_EC_GENERIC_DB_SOFT_FAILURE,
     687             :                                          "get_coin_history");
     688           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     689           0 :       return TALER_MHD_reply_with_error (rc->connection,
     690             :                                          MHD_HTTP_NOT_FOUND,
     691             :                                          TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN,
     692             :                                          NULL);
     693           4 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     694             :       /* Handled below */
     695           4 :       break;
     696             :     }
     697             :   }
     698             : 
     699           4 :   GNUNET_snprintf (etagp,
     700             :                    sizeof (etagp),
     701             :                    "\"%llu\"",
     702             :                    (unsigned long long) etag_out);
     703           4 :   if (etag_in == etag_out)
     704             :   {
     705           0 :     return TEH_RESPONSE_reply_not_modified (rc->connection,
     706             :                                             etagp,
     707             :                                             &add_response_headers,
     708             :                                             NULL);
     709             :   }
     710           4 :   if (NULL == tl)
     711             :   {
     712             :     /* 204: empty history */
     713           0 :     resp = MHD_create_response_from_buffer_static (0,
     714             :                                                    "");
     715           0 :     http_status = MHD_HTTP_NO_CONTENT;
     716             :   }
     717             :   else
     718             :   {
     719             :     /* 200: regular history */
     720             :     json_t *history;
     721             : 
     722           4 :     history = compile_transaction_history (coin_pub,
     723             :                                            tl);
     724           4 :     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
     725             :                                             tl);
     726           4 :     tl = NULL;
     727           4 :     if (NULL == history)
     728             :     {
     729           0 :       GNUNET_break (0);
     730           0 :       return TALER_MHD_reply_with_error (rc->connection,
     731             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     732             :                                          TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
     733             :                                          "Failed to compile coin history");
     734             :     }
     735           4 :     resp = TALER_MHD_MAKE_JSON_PACK (
     736             :       GNUNET_JSON_pack_data_auto ("h_denom_pub",
     737             :                                   &h_denom_pub),
     738             :       TALER_JSON_pack_amount ("balance",
     739             :                               &balance),
     740             :       GNUNET_JSON_pack_array_steal ("history",
     741             :                                     history));
     742           4 :     http_status = MHD_HTTP_OK;
     743             :   }
     744           4 :   add_response_headers (NULL,
     745             :                         resp);
     746           4 :   GNUNET_break (MHD_YES ==
     747             :                 MHD_add_response_header (resp,
     748             :                                          MHD_HTTP_HEADER_ETAG,
     749             :                                          etagp));
     750             :   {
     751             :     MHD_RESULT ret;
     752             : 
     753           4 :     ret = MHD_queue_response (rc->connection,
     754             :                               http_status,
     755             :                               resp);
     756           4 :     GNUNET_break (MHD_YES == ret);
     757           4 :     MHD_destroy_response (resp);
     758           4 :     return ret;
     759             :   }
     760             : }
     761             : 
     762             : 
     763             : /* end of taler-exchange-httpd_coins_get.c */

Generated by: LCOV version 1.16