LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_responses.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 147 277 53.1 %
Date: 2021-08-30 06:43:37 Functions: 4 5 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2021 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_responses.c
      18             :  * @brief API for generating generic replies of the exchange; these
      19             :  *        functions are called TEH_RESPONSE_reply_ and they generate
      20             :  *        and queue MHD response objects for a given connection.
      21             :  * @author Florian Dold
      22             :  * @author Benedikt Mueller
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include <zlib.h>
      27             : #include "taler-exchange-httpd_responses.h"
      28             : #include "taler_util.h"
      29             : #include "taler_json_lib.h"
      30             : #include "taler_mhd_lib.h"
      31             : #include "taler-exchange-httpd_keys.h"
      32             : 
      33             : 
      34             : /**
      35             :  * Compile the transaction history of a coin into a JSON object.
      36             :  *
      37             :  * @param coin_pub public key of the coin
      38             :  * @param tl transaction history to JSON-ify
      39             :  * @return json representation of the @a rh, NULL on error
      40             :  */
      41             : json_t *
      42          14 : TEH_RESPONSE_compile_transaction_history (
      43             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
      44             :   const struct TALER_EXCHANGEDB_TransactionList *tl)
      45             : {
      46             :   json_t *history;
      47             : 
      48          14 :   history = json_array ();
      49          14 :   if (NULL == history)
      50             :   {
      51           0 :     GNUNET_break (0); /* out of memory!? */
      52           0 :     return NULL;
      53             :   }
      54          41 :   for (const struct TALER_EXCHANGEDB_TransactionList *pos = tl;
      55             :        NULL != pos;
      56          27 :        pos = pos->next)
      57             :   {
      58          27 :     switch (pos->type)
      59             :     {
      60          13 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
      61             :       {
      62          13 :         const struct TALER_EXCHANGEDB_DepositListEntry *deposit =
      63             :           pos->details.deposit;
      64          26 :         struct TALER_DepositRequestPS dr = {
      65          13 :           .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
      66          13 :           .purpose.size = htonl (sizeof (dr)),
      67             :           .h_contract_terms = deposit->h_contract_terms,
      68             :           .h_wire = deposit->h_wire,
      69             :           .h_denom_pub = deposit->h_denom_pub,
      70          13 :           .wallet_timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp),
      71          13 :           .refund_deadline = GNUNET_TIME_absolute_hton (
      72             :             deposit->refund_deadline),
      73             :           .merchant = deposit->merchant_pub,
      74             :           .coin_pub = *coin_pub
      75             :         };
      76             : 
      77          13 :         TALER_amount_hton (&dr.amount_with_fee,
      78             :                            &deposit->amount_with_fee);
      79          13 :         TALER_amount_hton (&dr.deposit_fee,
      80             :                            &deposit->deposit_fee);
      81             : #if ENABLE_SANITY_CHECKS
      82             :         /* internal sanity check before we hand out a bogus sig... */
      83          13 :         if (GNUNET_OK !=
      84          13 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
      85             :                                         &dr,
      86             :                                         &deposit->csig.eddsa_signature,
      87             :                                         &coin_pub->eddsa_pub))
      88             :         {
      89           0 :           GNUNET_break (0);
      90           0 :           json_decref (history);
      91           0 :           return NULL;
      92             :         }
      93             : #endif
      94          13 :         if (0 !=
      95          13 :             json_array_append_new (
      96             :               history,
      97          13 :               GNUNET_JSON_PACK (
      98             :                 GNUNET_JSON_pack_string ("type",
      99             :                                          "DEPOSIT"),
     100             :                 TALER_JSON_pack_amount ("amount",
     101             :                                         &deposit->amount_with_fee),
     102             :                 TALER_JSON_pack_amount ("deposit_fee",
     103             :                                         &deposit->deposit_fee),
     104             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     105             :                                            deposit->timestamp),
     106             :                 GNUNET_JSON_pack_allow_null (
     107             :                   GNUNET_JSON_pack_time_abs ("refund_deadline",
     108             :                                              deposit->refund_deadline)),
     109             :                 GNUNET_JSON_pack_data_auto ("merchant_pub",
     110             :                                             &deposit->merchant_pub),
     111             :                 GNUNET_JSON_pack_data_auto ("h_contract_terms",
     112             :                                             &deposit->h_contract_terms),
     113             :                 GNUNET_JSON_pack_data_auto ("h_wire",
     114             :                                             &deposit->h_wire),
     115             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     116             :                                             &deposit->h_denom_pub),
     117             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     118             :                                             &deposit->csig))))
     119             :         {
     120           0 :           GNUNET_break (0);
     121           0 :           json_decref (history);
     122           0 :           return NULL;
     123             :         }
     124          13 :         break;
     125             :       }
     126          11 :     case TALER_EXCHANGEDB_TT_MELT:
     127             :       {
     128          11 :         const struct TALER_EXCHANGEDB_MeltListEntry *melt =
     129             :           pos->details.melt;
     130          11 :         struct TALER_RefreshMeltCoinAffirmationPS ms = {
     131          11 :           .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT),
     132          11 :           .purpose.size = htonl (sizeof (ms)),
     133             :           .rc = melt->rc,
     134             :           .h_denom_pub = melt->h_denom_pub,
     135             :           .coin_pub = *coin_pub
     136             :         };
     137             : 
     138          11 :         TALER_amount_hton (&ms.amount_with_fee,
     139             :                            &melt->amount_with_fee);
     140          11 :         TALER_amount_hton (&ms.melt_fee,
     141             :                            &melt->melt_fee);
     142             : #if ENABLE_SANITY_CHECKS
     143             :         /* internal sanity check before we hand out a bogus sig... */
     144          11 :         if (GNUNET_OK !=
     145          11 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
     146             :                                         &ms,
     147             :                                         &melt->coin_sig.eddsa_signature,
     148             :                                         &coin_pub->eddsa_pub))
     149             :         {
     150           0 :           GNUNET_break (0);
     151           0 :           json_decref (history);
     152           0 :           return NULL;
     153             :         }
     154             : #endif
     155          11 :         if (0 !=
     156          11 :             json_array_append_new (
     157             :               history,
     158          11 :               GNUNET_JSON_PACK (
     159             :                 GNUNET_JSON_pack_string ("type",
     160             :                                          "MELT"),
     161             :                 TALER_JSON_pack_amount ("amount",
     162             :                                         &melt->amount_with_fee),
     163             :                 TALER_JSON_pack_amount ("melt_fee",
     164             :                                         &melt->melt_fee),
     165             :                 GNUNET_JSON_pack_data_auto ("rc",
     166             :                                             &melt->rc),
     167             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     168             :                                             &melt->h_denom_pub),
     169             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     170             :                                             &melt->coin_sig))))
     171             :         {
     172           0 :           GNUNET_break (0);
     173           0 :           json_decref (history);
     174           0 :           return NULL;
     175             :         }
     176             :       }
     177          11 :       break;
     178           2 :     case TALER_EXCHANGEDB_TT_REFUND:
     179             :       {
     180           2 :         const struct TALER_EXCHANGEDB_RefundListEntry *refund =
     181             :           pos->details.refund;
     182             :         struct TALER_Amount value;
     183           4 :         struct TALER_RefundRequestPS rr = {
     184           2 :           .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
     185           2 :           .purpose.size = htonl (sizeof (rr)),
     186             :           .h_contract_terms = refund->h_contract_terms,
     187             :           .coin_pub = *coin_pub,
     188             :           .merchant = refund->merchant_pub,
     189           2 :           .rtransaction_id = GNUNET_htonll (refund->rtransaction_id)
     190             :         };
     191             : 
     192           2 :         TALER_amount_hton (&rr.refund_amount,
     193             :                            &refund->refund_amount);
     194             : #if ENABLE_SANITY_CHECKS
     195             :         /* internal sanity check before we hand out a bogus sig... */
     196           2 :         if (GNUNET_OK !=
     197           2 :             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
     198             :                                         &rr,
     199             :                                         &refund->merchant_sig.eddsa_sig,
     200             :                                         &refund->merchant_pub.eddsa_pub))
     201             :         {
     202           0 :           GNUNET_break (0);
     203           0 :           json_decref (history);
     204           0 :           return NULL;
     205             :         }
     206             : #endif
     207           2 :         if (0 >
     208           2 :             TALER_amount_subtract (&value,
     209             :                                    &refund->refund_amount,
     210             :                                    &refund->refund_fee))
     211             :         {
     212           0 :           GNUNET_break (0);
     213           0 :           json_decref (history);
     214           0 :           return NULL;
     215             :         }
     216           2 :         if (0 !=
     217           2 :             json_array_append_new (
     218             :               history,
     219           2 :               GNUNET_JSON_PACK (
     220             :                 GNUNET_JSON_pack_string ("type",
     221             :                                          "REFUND"),
     222             :                 TALER_JSON_pack_amount ("amount",
     223             :                                         &value),
     224             :                 TALER_JSON_pack_amount ("refund_fee",
     225             :                                         &refund->refund_fee),
     226             :                 GNUNET_JSON_pack_data_auto ("h_contract_terms",
     227             :                                             &refund->h_contract_terms),
     228             :                 GNUNET_JSON_pack_data_auto ("merchant_pub",
     229             :                                             &refund->merchant_pub),
     230             :                 GNUNET_JSON_pack_uint64 ("rtransaction_id",
     231             :                                          refund->rtransaction_id),
     232             :                 GNUNET_JSON_pack_data_auto ("merchant_sig",
     233             :                                             &refund->merchant_sig))))
     234             :         {
     235           0 :           GNUNET_break (0);
     236           0 :           json_decref (history);
     237           0 :           return NULL;
     238             :         }
     239             :       }
     240           2 :       break;
     241           1 :     case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
     242             :       {
     243           1 :         struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
     244             :           pos->details.old_coin_recoup;
     245             :         struct TALER_ExchangePublicKeyP epub;
     246             :         struct TALER_ExchangeSignatureP esig;
     247           2 :         struct TALER_RecoupRefreshConfirmationPS pc = {
     248           1 :           .purpose.purpose = htonl (
     249             :             TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
     250           1 :           .purpose.size = htonl (sizeof (pc)),
     251           1 :           .timestamp = GNUNET_TIME_absolute_hton (pr->timestamp),
     252             :           .coin_pub = pr->coin.coin_pub,
     253             :           .old_coin_pub = pr->old_coin_pub
     254             :         };
     255             : 
     256           1 :         TALER_amount_hton (&pc.recoup_amount,
     257           1 :                            &pr->value);
     258           1 :         if (TALER_EC_NONE !=
     259           1 :             TEH_keys_exchange_sign (&pc,
     260             :                                     &epub,
     261             :                                     &esig))
     262             :         {
     263           0 :           GNUNET_break (0);
     264           0 :           json_decref (history);
     265           0 :           return NULL;
     266             :         }
     267             :         /* NOTE: we could also provide coin_pub's coin_sig, denomination key hash and
     268             :            the denomination key's RSA signature over coin_pub, but as the
     269             :            wallet should really already have this information (and cannot
     270             :            check or do anything with it anyway if it doesn't), it seems
     271             :            strictly unnecessary. */
     272           1 :         if (0 !=
     273           1 :             json_array_append_new (
     274             :               history,
     275           1 :               GNUNET_JSON_PACK (
     276             :                 GNUNET_JSON_pack_string ("type",
     277             :                                          "OLD-COIN-RECOUP"),
     278             :                 TALER_JSON_pack_amount ("amount",
     279             :                                         &pr->value),
     280             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     281             :                                             &esig),
     282             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     283             :                                             &epub),
     284             :                 GNUNET_JSON_pack_data_auto ("coin_pub",
     285             :                                             &pr->coin.coin_pub),
     286             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     287             :                                            pr->timestamp))))
     288             :         {
     289           0 :           GNUNET_break (0);
     290           0 :           json_decref (history);
     291           0 :           return NULL;
     292             :         }
     293           1 :         break;
     294             :       }
     295           0 :     case TALER_EXCHANGEDB_TT_RECOUP:
     296             :       {
     297           0 :         const struct TALER_EXCHANGEDB_RecoupListEntry *recoup =
     298             :           pos->details.recoup;
     299             :         struct TALER_ExchangePublicKeyP epub;
     300             :         struct TALER_ExchangeSignatureP esig;
     301           0 :         struct TALER_RecoupConfirmationPS pc = {
     302           0 :           .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
     303           0 :           .purpose.size = htonl (sizeof (pc)),
     304           0 :           .timestamp = GNUNET_TIME_absolute_hton (recoup->timestamp),
     305             :           .coin_pub = *coin_pub,
     306             :           .reserve_pub = recoup->reserve_pub
     307             :         };
     308             : 
     309           0 :         TALER_amount_hton (&pc.recoup_amount,
     310             :                            &recoup->value);
     311           0 :         if (TALER_EC_NONE !=
     312           0 :             TEH_keys_exchange_sign (&pc,
     313             :                                     &epub,
     314             :                                     &esig))
     315             :         {
     316           0 :           GNUNET_break (0);
     317           0 :           json_decref (history);
     318           0 :           return NULL;
     319             :         }
     320           0 :         if (0 !=
     321           0 :             json_array_append_new (
     322             :               history,
     323           0 :               GNUNET_JSON_PACK (
     324             :                 GNUNET_JSON_pack_string ("type",
     325             :                                          "RECOUP"),
     326             :                 TALER_JSON_pack_amount ("amount",
     327             :                                         &recoup->value),
     328             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     329             :                                             &esig),
     330             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     331             :                                             &epub),
     332             :                 GNUNET_JSON_pack_data_auto ("reserve_pub",
     333             :                                             &recoup->reserve_pub),
     334             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     335             :                                             &recoup->h_denom_pub),
     336             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     337             :                                             &recoup->coin_sig),
     338             :                 GNUNET_JSON_pack_data_auto ("coin_blind",
     339             :                                             &recoup->coin_blind),
     340             :                 GNUNET_JSON_pack_data_auto ("reserve_pub",
     341             :                                             &recoup->reserve_pub),
     342             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     343             :                                            recoup->timestamp))))
     344             :         {
     345           0 :           GNUNET_break (0);
     346           0 :           json_decref (history);
     347           0 :           return NULL;
     348             :         }
     349             :       }
     350           0 :       break;
     351           0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
     352             :       {
     353           0 :         struct TALER_EXCHANGEDB_RecoupRefreshListEntry *pr =
     354             :           pos->details.recoup_refresh;
     355             :         struct TALER_ExchangePublicKeyP epub;
     356             :         struct TALER_ExchangeSignatureP esig;
     357           0 :         struct TALER_RecoupRefreshConfirmationPS pc = {
     358           0 :           .purpose.purpose = htonl (
     359             :             TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
     360           0 :           .purpose.size = htonl (sizeof (pc)),
     361           0 :           .timestamp = GNUNET_TIME_absolute_hton (pr->timestamp),
     362             :           .coin_pub = *coin_pub,
     363             :           .old_coin_pub = pr->old_coin_pub
     364             :         };
     365             : 
     366           0 :         TALER_amount_hton (&pc.recoup_amount,
     367           0 :                            &pr->value);
     368           0 :         if (TALER_EC_NONE !=
     369           0 :             TEH_keys_exchange_sign (&pc,
     370             :                                     &epub,
     371             :                                     &esig))
     372             :         {
     373           0 :           GNUNET_break (0);
     374           0 :           json_decref (history);
     375           0 :           return NULL;
     376             :         }
     377             :         /* NOTE: we could also provide coin_pub's coin_sig, denomination key
     378             :            hash and the denomination key's RSA signature over coin_pub, but as
     379             :            the wallet should really already have this information (and cannot
     380             :            check or do anything with it anyway if it doesn't), it seems
     381             :            strictly unnecessary. *///
     382           0 :         if (0 !=
     383           0 :             json_array_append_new (
     384             :               history,
     385           0 :               GNUNET_JSON_PACK (
     386             :                 GNUNET_JSON_pack_string ("type",
     387             :                                          "RECOUP-REFRESH"),
     388             :                 TALER_JSON_pack_amount ("amount",
     389             :                                         &pr->value),
     390             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     391             :                                             &esig),
     392             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     393             :                                             &epub),
     394             :                 GNUNET_JSON_pack_data_auto ("old_coin_pub",
     395             :                                             &pr->old_coin_pub),
     396             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     397             :                                             &pr->coin.denom_pub_hash),
     398             :                 GNUNET_JSON_pack_data_auto ("coin_sig",
     399             :                                             &pr->coin_sig),
     400             :                 GNUNET_JSON_pack_data_auto ("coin_blind",
     401             :                                             &pr->coin_blind),
     402             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     403             :                                            pr->timestamp))))
     404             :         {
     405           0 :           GNUNET_break (0);
     406           0 :           json_decref (history);
     407           0 :           return NULL;
     408             :         }
     409           0 :         break;
     410             :       }
     411           0 :     default:
     412           0 :       GNUNET_assert (0);
     413             :     }
     414             :   }
     415          14 :   return history;
     416             : }
     417             : 
     418             : 
     419             : MHD_RESULT
     420           0 : TEH_RESPONSE_reply_unknown_denom_pub_hash (
     421             :   struct MHD_Connection *connection,
     422             :   const struct GNUNET_HashCode *dph)
     423             : {
     424             :   struct TALER_ExchangePublicKeyP epub;
     425             :   struct TALER_ExchangeSignatureP esig;
     426             :   struct GNUNET_TIME_Absolute now;
     427             :   enum TALER_ErrorCode ec;
     428             : 
     429           0 :   now = GNUNET_TIME_absolute_get ();
     430           0 :   GNUNET_TIME_round_abs (&now);
     431             :   {
     432           0 :     struct TALER_DenominationUnknownAffirmationPS dua = {
     433           0 :       .purpose.size = htonl (sizeof (dua)),
     434           0 :       .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN),
     435           0 :       .timestamp = GNUNET_TIME_absolute_hton (now),
     436             :       .h_denom_pub = *dph,
     437             :     };
     438             : 
     439           0 :     ec = TEH_keys_exchange_sign (&dua,
     440             :                                  &epub,
     441             :                                  &esig);
     442             :   }
     443           0 :   if (TALER_EC_NONE != ec)
     444             :   {
     445           0 :     GNUNET_break (0);
     446           0 :     return TALER_MHD_reply_with_error (connection,
     447             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     448             :                                        ec,
     449             :                                        NULL);
     450             :   }
     451           0 :   return TALER_MHD_REPLY_JSON_PACK (
     452             :     connection,
     453             :     MHD_HTTP_NOT_FOUND,
     454             :     TALER_JSON_pack_ec (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN),
     455             :     GNUNET_JSON_pack_time_abs ("timestamp",
     456             :                                now),
     457             :     GNUNET_JSON_pack_data_auto ("exchange_pub",
     458             :                                 &epub),
     459             :     GNUNET_JSON_pack_data_auto ("exchange_sig",
     460             :                                 &esig),
     461             :     GNUNET_JSON_pack_data_auto ("h_denom_pub",
     462             :                                 dph));
     463             : }
     464             : 
     465             : 
     466             : MHD_RESULT
     467           4 : TEH_RESPONSE_reply_expired_denom_pub_hash (
     468             :   struct MHD_Connection *connection,
     469             :   const struct GNUNET_HashCode *dph,
     470             :   struct GNUNET_TIME_Absolute now,
     471             :   enum TALER_ErrorCode ec,
     472             :   const char *oper)
     473             : {
     474             :   struct TALER_ExchangePublicKeyP epub;
     475             :   struct TALER_ExchangeSignatureP esig;
     476             :   enum TALER_ErrorCode ecr;
     477           8 :   struct TALER_DenominationExpiredAffirmationPS dua = {
     478           4 :     .purpose.size = htonl (sizeof (dua)),
     479           4 :     .purpose.purpose = htonl (
     480             :       TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED),
     481           4 :     .timestamp = GNUNET_TIME_absolute_hton (now),
     482             :     .h_denom_pub = *dph,
     483             :   };
     484             : 
     485             :   /* strncpy would create a compiler warning */
     486           0 :   memcpy (dua.operation,
     487             :           oper,
     488           4 :           GNUNET_MIN (sizeof (dua.operation),
     489             :                       strlen (oper)));
     490           4 :   ecr = TEH_keys_exchange_sign (&dua,
     491             :                                 &epub,
     492             :                                 &esig);
     493           4 :   if (TALER_EC_NONE != ecr)
     494             :   {
     495           0 :     GNUNET_break (0);
     496           0 :     return TALER_MHD_reply_with_error (connection,
     497             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     498             :                                        ec,
     499             :                                        NULL);
     500             :   }
     501           4 :   return TALER_MHD_REPLY_JSON_PACK (
     502             :     connection,
     503             :     MHD_HTTP_GONE,
     504             :     TALER_JSON_pack_ec (ec),
     505             :     GNUNET_JSON_pack_string ("oper",
     506             :                              oper),
     507             :     GNUNET_JSON_pack_time_abs ("timestamp",
     508             :                                now),
     509             :     GNUNET_JSON_pack_data_auto ("exchange_pub",
     510             :                                 &epub),
     511             :     GNUNET_JSON_pack_data_auto ("exchange_sig",
     512             :                                 &esig),
     513             :     GNUNET_JSON_pack_data_auto ("h_denom_pub",
     514             :                                 dph));
     515             : }
     516             : 
     517             : 
     518             : /**
     519             :  * Send proof that a request is invalid to client because of
     520             :  * insufficient funds.  This function will create a message with all
     521             :  * of the operations affecting the coin that demonstrate that the coin
     522             :  * has insufficient value.
     523             :  *
     524             :  * @param connection connection to the client
     525             :  * @param ec error code to return
     526             :  * @param coin_pub public key of the coin
     527             :  * @param tl transaction list to use to build reply
     528             :  * @return MHD result code
     529             :  */
     530             : MHD_RESULT
     531           9 : TEH_RESPONSE_reply_coin_insufficient_funds (
     532             :   struct MHD_Connection *connection,
     533             :   enum TALER_ErrorCode ec,
     534             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     535             :   const struct TALER_EXCHANGEDB_TransactionList *tl)
     536             : {
     537             :   json_t *history;
     538             : 
     539           9 :   history = TEH_RESPONSE_compile_transaction_history (coin_pub,
     540             :                                                       tl);
     541           9 :   if (NULL == history)
     542             :   {
     543           0 :     GNUNET_break (0);
     544           0 :     return TALER_MHD_reply_with_error (connection,
     545             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     546             :                                        TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
     547             :                                        "Failed to generated proof of insufficient funds");
     548             :   }
     549           9 :   return TALER_MHD_REPLY_JSON_PACK (
     550             :     connection,
     551             :     MHD_HTTP_CONFLICT,
     552             :     TALER_JSON_pack_ec (ec),
     553             :     GNUNET_JSON_pack_array_steal ("history",
     554             :                                   history));
     555             : }
     556             : 
     557             : 
     558             : /**
     559             :  * Compile the history of a reserve into a JSON object
     560             :  * and calculate the total balance.
     561             :  *
     562             :  * @param rh reserve history to JSON-ify
     563             :  * @param[out] balance set to current reserve balance
     564             :  * @return json representation of the @a rh, NULL on error
     565             :  */
     566             : json_t *
     567          11 : TEH_RESPONSE_compile_reserve_history (
     568             :   const struct TALER_EXCHANGEDB_ReserveHistory *rh,
     569             :   struct TALER_Amount *balance)
     570             : {
     571             :   struct TALER_Amount credit_total;
     572             :   struct TALER_Amount withdraw_total;
     573             :   json_t *json_history;
     574             :   enum InitAmounts
     575             :   {
     576             :     /** Nothing initialized */
     577             :     IA_NONE = 0,
     578             :     /** credit_total initialized */
     579             :     IA_CREDIT = 1,
     580             :     /** withdraw_total initialized */
     581             :     IA_WITHDRAW = 2
     582          11 :   } init = IA_NONE;
     583             : 
     584          11 :   json_history = json_array ();
     585          45 :   for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
     586             :        NULL != pos;
     587          34 :        pos = pos->next)
     588             :   {
     589          34 :     switch (pos->type)
     590             :     {
     591          11 :     case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
     592             :       {
     593          11 :         const struct TALER_EXCHANGEDB_BankTransfer *bank =
     594             :           pos->details.bank;
     595          11 :         if (0 == (IA_CREDIT & init))
     596             :         {
     597          11 :           credit_total = bank->amount;
     598          11 :           init |= IA_CREDIT;
     599             :         }
     600           0 :         else if (0 >
     601           0 :                  TALER_amount_add (&credit_total,
     602             :                                    &credit_total,
     603             :                                    &bank->amount))
     604             :         {
     605           0 :           GNUNET_break (0);
     606           0 :           json_decref (json_history);
     607           0 :           return NULL;
     608             :         }
     609          11 :         if (0 !=
     610          11 :             json_array_append_new (
     611             :               json_history,
     612          11 :               GNUNET_JSON_PACK (
     613             :                 GNUNET_JSON_pack_string ("type",
     614             :                                          "CREDIT"),
     615             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     616             :                                            bank->execution_date),
     617             :                 GNUNET_JSON_pack_string ("sender_account_url",
     618             :                                          bank->sender_account_details),
     619             :                 GNUNET_JSON_pack_uint64 ("wire_reference",
     620             :                                          bank->wire_reference),
     621             :                 TALER_JSON_pack_amount ("amount",
     622             :                                         &bank->amount))))
     623             :         {
     624           0 :           GNUNET_break (0);
     625           0 :           json_decref (json_history);
     626           0 :           return NULL;
     627             :         }
     628          11 :         break;
     629             :       }
     630          14 :     case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
     631             :       {
     632          14 :         const struct TALER_EXCHANGEDB_CollectableBlindcoin *withdraw
     633             :           = pos->details.withdraw;
     634             :         struct TALER_Amount value;
     635             : 
     636          14 :         value = withdraw->amount_with_fee;
     637          14 :         if (0 == (IA_WITHDRAW & init))
     638             :         {
     639           6 :           withdraw_total = value;
     640           6 :           init |= IA_WITHDRAW;
     641             :         }
     642             :         else
     643             :         {
     644           8 :           if (0 >
     645           8 :               TALER_amount_add (&withdraw_total,
     646             :                                 &withdraw_total,
     647             :                                 &value))
     648             :           {
     649           0 :             GNUNET_break (0);
     650           0 :             json_decref (json_history);
     651           0 :             return NULL;
     652             :           }
     653             :         }
     654          14 :         if (0 !=
     655          14 :             json_array_append_new (
     656             :               json_history,
     657          14 :               GNUNET_JSON_PACK (
     658             :                 GNUNET_JSON_pack_string ("type",
     659             :                                          "WITHDRAW"),
     660             :                 GNUNET_JSON_pack_data_auto ("reserve_sig",
     661             :                                             &withdraw->reserve_sig),
     662             :                 GNUNET_JSON_pack_data_auto ("h_coin_envelope",
     663             :                                             &withdraw->h_coin_envelope),
     664             :                 GNUNET_JSON_pack_data_auto ("h_denom_pub",
     665             :                                             &withdraw->denom_pub_hash),
     666             :                 TALER_JSON_pack_amount ("withdraw_fee",
     667             :                                         &withdraw->withdraw_fee),
     668             :                 TALER_JSON_pack_amount ("amount",
     669             :                                         &value))))
     670             :         {
     671           0 :           GNUNET_break (0);
     672           0 :           json_decref (json_history);
     673           0 :           return NULL;
     674             :         }
     675             :       }
     676          14 :       break;
     677           4 :     case TALER_EXCHANGEDB_RO_RECOUP_COIN:
     678             :       {
     679           4 :         const struct TALER_EXCHANGEDB_Recoup *recoup
     680             :           = pos->details.recoup;
     681             :         struct TALER_ExchangePublicKeyP pub;
     682             :         struct TALER_ExchangeSignatureP sig;
     683             : 
     684           4 :         if (0 == (IA_CREDIT & init))
     685             :         {
     686           0 :           credit_total = recoup->value;
     687           0 :           init |= IA_CREDIT;
     688             :         }
     689           4 :         else if (0 >
     690           4 :                  TALER_amount_add (&credit_total,
     691             :                                    &credit_total,
     692             :                                    &recoup->value))
     693             :         {
     694           0 :           GNUNET_break (0);
     695           0 :           json_decref (json_history);
     696           0 :           return NULL;
     697             :         }
     698             :         {
     699           8 :           struct TALER_RecoupConfirmationPS pc = {
     700           4 :             .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
     701           4 :             .purpose.size = htonl (sizeof (pc)),
     702           4 :             .timestamp = GNUNET_TIME_absolute_hton (recoup->timestamp),
     703             :             .coin_pub = recoup->coin.coin_pub,
     704             :             .reserve_pub = recoup->reserve_pub
     705             :           };
     706             : 
     707           4 :           TALER_amount_hton (&pc.recoup_amount,
     708             :                              &recoup->value);
     709           4 :           if (TALER_EC_NONE !=
     710           4 :               TEH_keys_exchange_sign (&pc,
     711             :                                       &pub,
     712             :                                       &sig))
     713             :           {
     714           0 :             GNUNET_break (0);
     715           0 :             json_decref (json_history);
     716           0 :             return NULL;
     717             :           }
     718             :         }
     719             : 
     720           4 :         if (0 !=
     721           4 :             json_array_append_new (
     722             :               json_history,
     723           4 :               GNUNET_JSON_PACK (
     724             :                 GNUNET_JSON_pack_string ("type",
     725             :                                          "RECOUP"),
     726             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     727             :                                             &pub),
     728             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     729             :                                             &sig),
     730             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     731             :                                            recoup->timestamp),
     732             :                 TALER_JSON_pack_amount ("amount",
     733             :                                         &recoup->value),
     734             :                 GNUNET_JSON_pack_data_auto ("coin_pub",
     735             :                                             &recoup->coin.coin_pub))))
     736             :         {
     737           0 :           GNUNET_break (0);
     738           0 :           json_decref (json_history);
     739           0 :           return NULL;
     740             :         }
     741             :       }
     742           4 :       break;
     743           5 :     case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
     744             :       {
     745           5 :         const struct TALER_EXCHANGEDB_ClosingTransfer *closing =
     746             :           pos->details.closing;
     747             :         struct TALER_ExchangePublicKeyP pub;
     748             :         struct TALER_ExchangeSignatureP sig;
     749             :         struct TALER_Amount value;
     750             : 
     751           5 :         value = closing->amount;
     752           5 :         if (0 == (IA_WITHDRAW & init))
     753             :         {
     754           5 :           withdraw_total = value;
     755           5 :           init |= IA_WITHDRAW;
     756             :         }
     757             :         else
     758             :         {
     759           0 :           if (0 >
     760           0 :               TALER_amount_add (&withdraw_total,
     761             :                                 &withdraw_total,
     762             :                                 &value))
     763             :           {
     764           0 :             GNUNET_break (0);
     765           0 :             json_decref (json_history);
     766           0 :             return NULL;
     767             :           }
     768             :         }
     769             :         {
     770          10 :           struct TALER_ReserveCloseConfirmationPS rcc = {
     771           5 :             .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED),
     772           5 :             .purpose.size = htonl (sizeof (rcc)),
     773           5 :             .timestamp = GNUNET_TIME_absolute_hton (closing->execution_date),
     774           5 :             .reserve_pub = pos->details.closing->reserve_pub,
     775             :             .wtid = closing->wtid
     776             :           };
     777             : 
     778           5 :           TALER_amount_hton (&rcc.closing_amount,
     779             :                              &value);
     780           5 :           TALER_amount_hton (&rcc.closing_fee,
     781             :                              &closing->closing_fee);
     782           5 :           GNUNET_CRYPTO_hash (closing->receiver_account_details,
     783           5 :                               strlen (closing->receiver_account_details) + 1,
     784             :                               &rcc.h_wire);
     785           5 :           if (TALER_EC_NONE !=
     786           5 :               TEH_keys_exchange_sign (&rcc,
     787             :                                       &pub,
     788             :                                       &sig))
     789             :           {
     790           0 :             GNUNET_break (0);
     791           0 :             json_decref (json_history);
     792           0 :             return NULL;
     793             :           }
     794             :         }
     795           5 :         if (0 !=
     796           5 :             json_array_append_new (
     797             :               json_history,
     798           5 :               GNUNET_JSON_PACK (
     799             :                 GNUNET_JSON_pack_string ("type",
     800             :                                          "CLOSING"),
     801             :                 GNUNET_JSON_pack_string ("receiver_account_details",
     802             :                                          closing->receiver_account_details),
     803             :                 GNUNET_JSON_pack_data_auto ("wtid",
     804             :                                             &closing->wtid),
     805             :                 GNUNET_JSON_pack_data_auto ("exchange_pub",
     806             :                                             &pub),
     807             :                 GNUNET_JSON_pack_data_auto ("exchange_sig",
     808             :                                             &sig),
     809             :                 GNUNET_JSON_pack_time_abs ("timestamp",
     810             :                                            closing->execution_date),
     811             :                 TALER_JSON_pack_amount ("amount",
     812             :                                         &value),
     813             :                 TALER_JSON_pack_amount ("closing_fee",
     814             :                                         &closing->closing_fee))))
     815             :         {
     816           0 :           GNUNET_break (0);
     817           0 :           json_decref (json_history);
     818           0 :           return NULL;
     819             :         }
     820             :       }
     821           5 :       break;
     822             :     }
     823          34 :   }
     824             : 
     825          11 :   if (0 == (IA_CREDIT & init))
     826             :   {
     827             :     /* We should not have gotten here, without credits no reserve
     828             :        should exist! */
     829           0 :     GNUNET_break (0);
     830           0 :     json_decref (json_history);
     831           0 :     return NULL;
     832             :   }
     833          11 :   if (0 == (IA_WITHDRAW & init))
     834             :   {
     835             :     /* did not encounter any withdraw operations, set withdraw_total to zero */
     836           0 :     GNUNET_assert (GNUNET_OK ==
     837             :                    TALER_amount_set_zero (credit_total.currency,
     838             :                                           &withdraw_total));
     839             :   }
     840          11 :   if (0 >
     841          11 :       TALER_amount_subtract (balance,
     842             :                              &credit_total,
     843             :                              &withdraw_total))
     844             :   {
     845           0 :     GNUNET_break (0);
     846           0 :     json_decref (json_history);
     847           0 :     return NULL;
     848             :   }
     849             : 
     850          11 :   return json_history;
     851             : }
     852             : 
     853             : 
     854             : /* end of taler-exchange-httpd_responses.c */

Generated by: LCOV version 1.14