LCOV - code coverage report
Current view: top level - lib - exchange_api_common.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 681 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 27 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-2022 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see
      15             :   <http://www.gnu.org/licenses/>
      16             : */
      17             : /**
      18             :  * @file lib/exchange_api_common.c
      19             :  * @brief common functions for the exchange API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include "taler_json_lib.h"
      24             : #include <gnunet/gnunet_curl_lib.h>
      25             : #include "exchange_api_common.h"
      26             : #include "exchange_api_handle.h"
      27             : #include "taler_signatures.h"
      28             : 
      29             : 
      30             : /**
      31             :  * Context for history entry helpers.
      32             :  */
      33             : struct HistoryParseContext
      34             : {
      35             : 
      36             :   /**
      37             :    * Exchange we use.
      38             :    */
      39             :   struct TALER_EXCHANGE_Handle *exchange;
      40             : 
      41             :   /**
      42             :    * Our reserve public key.
      43             :    */
      44             :   const struct TALER_ReservePublicKeyP *reserve_pub;
      45             : 
      46             :   /**
      47             :    * Array of UUIDs.
      48             :    */
      49             :   struct GNUNET_HashCode *uuids;
      50             : 
      51             :   /**
      52             :    * Where to sum up total inbound amounts.
      53             :    */
      54             :   struct TALER_Amount *total_in;
      55             : 
      56             :   /**
      57             :    * Where to sum up total outbound amounts.
      58             :    */
      59             :   struct TALER_Amount *total_out;
      60             : 
      61             :   /**
      62             :    * Number of entries already used in @e uuids.
      63             :    */
      64             :   unsigned int uuid_off;
      65             : };
      66             : 
      67             : 
      68             : /**
      69             :  * Type of a function called to parse a reserve history
      70             :  * entry @a rh.
      71             :  *
      72             :  * @param[in,out] rh where to write the result
      73             :  * @param[in,out] uc UUID context for duplicate detection
      74             :  * @param transaction the transaction to parse
      75             :  * @return #GNUNET_OK on success
      76             :  */
      77             : typedef enum GNUNET_GenericReturnValue
      78             : (*ParseHelper)(struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
      79             :                struct HistoryParseContext *uc,
      80             :                const json_t *transaction);
      81             : 
      82             : 
      83             : /**
      84             :  * Parse "credit" reserve history entry.
      85             :  *
      86             :  * @param[in,out] rh entry to parse
      87             :  * @param uc our context
      88             :  * @param transaction the transaction to parse
      89             :  * @return #GNUNET_OK on success
      90             :  */
      91             : static enum GNUNET_GenericReturnValue
      92           0 : parse_credit (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
      93             :               struct HistoryParseContext *uc,
      94             :               const json_t *transaction)
      95             : {
      96             :   const char *wire_url;
      97             :   uint64_t wire_reference;
      98             :   struct GNUNET_TIME_Timestamp timestamp;
      99             :   struct GNUNET_JSON_Specification withdraw_spec[] = {
     100           0 :     GNUNET_JSON_spec_uint64 ("wire_reference",
     101             :                              &wire_reference),
     102           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     103             :                                 &timestamp),
     104           0 :     GNUNET_JSON_spec_string ("sender_account_url",
     105             :                              &wire_url),
     106           0 :     GNUNET_JSON_spec_end ()
     107             :   };
     108             : 
     109           0 :   rh->type = TALER_EXCHANGE_RTT_CREDIT;
     110           0 :   if (0 >
     111           0 :       TALER_amount_add (uc->total_in,
     112           0 :                         uc->total_in,
     113           0 :                         &rh->amount))
     114             :   {
     115             :     /* overflow in history already!? inconceivable! Bad exchange! */
     116           0 :     GNUNET_break_op (0);
     117           0 :     return GNUNET_SYSERR;
     118             :   }
     119           0 :   if (GNUNET_OK !=
     120           0 :       GNUNET_JSON_parse (transaction,
     121             :                          withdraw_spec,
     122             :                          NULL, NULL))
     123             :   {
     124           0 :     GNUNET_break_op (0);
     125           0 :     return GNUNET_SYSERR;
     126             :   }
     127           0 :   rh->details.in_details.sender_url = GNUNET_strdup (wire_url);
     128           0 :   rh->details.in_details.wire_reference = wire_reference;
     129           0 :   rh->details.in_details.timestamp = timestamp;
     130           0 :   return GNUNET_OK;
     131             : }
     132             : 
     133             : 
     134             : /**
     135             :  * Parse "credit" reserve history entry.
     136             :  *
     137             :  * @param[in,out] rh entry to parse
     138             :  * @param uc our context
     139             :  * @param transaction the transaction to parse
     140             :  * @return #GNUNET_OK on success
     141             :  */
     142             : static enum GNUNET_GenericReturnValue
     143           0 : parse_withdraw (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
     144             :                 struct HistoryParseContext *uc,
     145             :                 const json_t *transaction)
     146             : {
     147             :   struct TALER_ReserveSignatureP sig;
     148             :   struct TALER_DenominationHashP h_denom_pub;
     149             :   struct TALER_BlindedCoinHashP bch;
     150             :   struct TALER_Amount withdraw_fee;
     151             :   struct GNUNET_JSON_Specification withdraw_spec[] = {
     152           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     153             :                                  &sig),
     154           0 :     TALER_JSON_spec_amount_any ("withdraw_fee",
     155             :                                 &withdraw_fee),
     156           0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     157             :                                  &h_denom_pub),
     158           0 :     GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
     159             :                                  &bch),
     160           0 :     GNUNET_JSON_spec_end ()
     161             :   };
     162             : 
     163           0 :   rh->type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     164           0 :   if (GNUNET_OK !=
     165           0 :       GNUNET_JSON_parse (transaction,
     166             :                          withdraw_spec,
     167             :                          NULL, NULL))
     168             :   {
     169           0 :     GNUNET_break_op (0);
     170           0 :     return GNUNET_SYSERR;
     171             :   }
     172             : 
     173             :   /* Check that the signature is a valid withdraw request */
     174           0 :   if (GNUNET_OK !=
     175           0 :       TALER_wallet_withdraw_verify (&h_denom_pub,
     176           0 :                                     &rh->amount,
     177             :                                     &bch,
     178             :                                     uc->reserve_pub,
     179             :                                     &sig))
     180             :   {
     181           0 :     GNUNET_break_op (0);
     182           0 :     GNUNET_JSON_parse_free (withdraw_spec);
     183           0 :     return GNUNET_SYSERR;
     184             :   }
     185             :   /* check that withdraw fee matches expectations! */
     186             :   {
     187             :     const struct TALER_EXCHANGE_Keys *key_state;
     188             :     const struct TALER_EXCHANGE_DenomPublicKey *dki;
     189             : 
     190           0 :     key_state = TALER_EXCHANGE_get_keys (uc->exchange);
     191           0 :     dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
     192             :                                                        &h_denom_pub);
     193           0 :     if ( (GNUNET_YES !=
     194           0 :           TALER_amount_cmp_currency (&withdraw_fee,
     195           0 :                                      &dki->fees.withdraw)) ||
     196             :          (0 !=
     197           0 :           TALER_amount_cmp (&withdraw_fee,
     198             :                             &dki->fees.withdraw)) )
     199             :     {
     200           0 :       GNUNET_break_op (0);
     201           0 :       GNUNET_JSON_parse_free (withdraw_spec);
     202           0 :       return GNUNET_SYSERR;
     203             :     }
     204           0 :     rh->details.withdraw.fee = withdraw_fee;
     205             :   }
     206             :   rh->details.withdraw.out_authorization_sig
     207           0 :     = json_object_get (transaction,
     208             :                        "signature");
     209             :   /* Check check that the same withdraw transaction
     210             :        isn't listed twice by the exchange. We use the
     211             :        "uuid" array to remember the hashes of all
     212             :        signatures, and compare the hashes to find
     213             :        duplicates. */
     214           0 :   GNUNET_CRYPTO_hash (&sig,
     215             :                       sizeof (sig),
     216           0 :                       &uc->uuids[uc->uuid_off]);
     217           0 :   for (unsigned int i = 0; i<uc->uuid_off; i++)
     218             :   {
     219           0 :     if (0 == GNUNET_memcmp (&uc->uuids[uc->uuid_off],
     220             :                             &uc->uuids[i]))
     221             :     {
     222           0 :       GNUNET_break_op (0);
     223           0 :       GNUNET_JSON_parse_free (withdraw_spec);
     224           0 :       return GNUNET_SYSERR;
     225             :     }
     226             :   }
     227           0 :   uc->uuid_off++;
     228             : 
     229           0 :   if (0 >
     230           0 :       TALER_amount_add (uc->total_out,
     231           0 :                         uc->total_out,
     232           0 :                         &rh->amount))
     233             :   {
     234             :     /* overflow in history already!? inconceivable! Bad exchange! */
     235           0 :     GNUNET_break_op (0);
     236           0 :     GNUNET_JSON_parse_free (withdraw_spec);
     237           0 :     return GNUNET_SYSERR;
     238             :   }
     239           0 :   return GNUNET_OK;
     240             : }
     241             : 
     242             : 
     243             : /**
     244             :  * Parse "recoup" reserve history entry.
     245             :  *
     246             :  * @param[in,out] rh entry to parse
     247             :  * @param uc our context
     248             :  * @param transaction the transaction to parse
     249             :  * @return #GNUNET_OK on success
     250             :  */
     251             : static enum GNUNET_GenericReturnValue
     252           0 : parse_recoup (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
     253             :               struct HistoryParseContext *uc,
     254             :               const json_t *transaction)
     255             : {
     256             :   const struct TALER_EXCHANGE_Keys *key_state;
     257             :   struct GNUNET_JSON_Specification recoup_spec[] = {
     258           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     259             :                                  &rh->details.recoup_details.coin_pub),
     260           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     261             :                                  &rh->details.recoup_details.exchange_sig),
     262           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     263             :                                  &rh->details.recoup_details.exchange_pub),
     264           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     265             :                                 &rh->details.recoup_details.timestamp),
     266           0 :     GNUNET_JSON_spec_end ()
     267             :   };
     268             : 
     269           0 :   rh->type = TALER_EXCHANGE_RTT_RECOUP;
     270           0 :   if (GNUNET_OK !=
     271           0 :       GNUNET_JSON_parse (transaction,
     272             :                          recoup_spec,
     273             :                          NULL, NULL))
     274             :   {
     275           0 :     GNUNET_break_op (0);
     276           0 :     return GNUNET_SYSERR;
     277             :   }
     278           0 :   key_state = TALER_EXCHANGE_get_keys (uc->exchange);
     279           0 :   if (GNUNET_OK !=
     280           0 :       TALER_EXCHANGE_test_signing_key (key_state,
     281           0 :                                        &rh->details.
     282             :                                        recoup_details.exchange_pub))
     283             :   {
     284           0 :     GNUNET_break_op (0);
     285           0 :     return GNUNET_SYSERR;
     286             :   }
     287           0 :   if (GNUNET_OK !=
     288           0 :       TALER_exchange_online_confirm_recoup_verify (
     289             :         rh->details.recoup_details.timestamp,
     290           0 :         &rh->amount,
     291           0 :         &rh->details.recoup_details.coin_pub,
     292             :         uc->reserve_pub,
     293           0 :         &rh->details.recoup_details.exchange_pub,
     294           0 :         &rh->details.recoup_details.exchange_sig))
     295             :   {
     296           0 :     GNUNET_break_op (0);
     297           0 :     return GNUNET_SYSERR;
     298             :   }
     299           0 :   if (0 >
     300           0 :       TALER_amount_add (uc->total_in,
     301           0 :                         uc->total_in,
     302           0 :                         &rh->amount))
     303             :   {
     304             :     /* overflow in history already!? inconceivable! Bad exchange! */
     305           0 :     GNUNET_break_op (0);
     306           0 :     return GNUNET_SYSERR;
     307             :   }
     308           0 :   return GNUNET_OK;
     309             : }
     310             : 
     311             : 
     312             : /**
     313             :  * Parse "closing" reserve history entry.
     314             :  *
     315             :  * @param[in,out] rh entry to parse
     316             :  * @param uc our context
     317             :  * @param transaction the transaction to parse
     318             :  * @return #GNUNET_OK on success
     319             :  */
     320             : static enum GNUNET_GenericReturnValue
     321           0 : parse_closing (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
     322             :                struct HistoryParseContext *uc,
     323             :                const json_t *transaction)
     324             : {
     325             :   const struct TALER_EXCHANGE_Keys *key_state;
     326             :   struct GNUNET_JSON_Specification closing_spec[] = {
     327           0 :     GNUNET_JSON_spec_string (
     328             :       "receiver_account_details",
     329             :       &rh->details.close_details.receiver_account_details),
     330           0 :     GNUNET_JSON_spec_fixed_auto ("wtid",
     331             :                                  &rh->details.close_details.wtid),
     332           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     333             :                                  &rh->details.close_details.exchange_sig),
     334           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     335             :                                  &rh->details.close_details.exchange_pub),
     336           0 :     TALER_JSON_spec_amount_any ("closing_fee",
     337             :                                 &rh->details.close_details.fee),
     338           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     339             :                                 &rh->details.close_details.timestamp),
     340           0 :     GNUNET_JSON_spec_end ()
     341             :   };
     342             : 
     343           0 :   rh->type = TALER_EXCHANGE_RTT_CLOSE;
     344           0 :   if (GNUNET_OK !=
     345           0 :       GNUNET_JSON_parse (transaction,
     346             :                          closing_spec,
     347             :                          NULL, NULL))
     348             :   {
     349           0 :     GNUNET_break_op (0);
     350           0 :     return GNUNET_SYSERR;
     351             :   }
     352           0 :   key_state = TALER_EXCHANGE_get_keys (uc->exchange);
     353           0 :   if (GNUNET_OK !=
     354           0 :       TALER_EXCHANGE_test_signing_key (
     355             :         key_state,
     356           0 :         &rh->details.close_details.exchange_pub))
     357             :   {
     358           0 :     GNUNET_break_op (0);
     359           0 :     return GNUNET_SYSERR;
     360             :   }
     361           0 :   if (GNUNET_OK !=
     362           0 :       TALER_exchange_online_reserve_closed_verify (
     363             :         rh->details.close_details.timestamp,
     364           0 :         &rh->amount,
     365           0 :         &rh->details.close_details.fee,
     366             :         rh->details.close_details.receiver_account_details,
     367           0 :         &rh->details.close_details.wtid,
     368             :         uc->reserve_pub,
     369           0 :         &rh->details.close_details.exchange_pub,
     370           0 :         &rh->details.close_details.exchange_sig))
     371             :   {
     372           0 :     GNUNET_break_op (0);
     373           0 :     return GNUNET_SYSERR;
     374             :   }
     375           0 :   if (0 >
     376           0 :       TALER_amount_add (uc->total_out,
     377           0 :                         uc->total_out,
     378           0 :                         &rh->amount))
     379             :   {
     380             :     /* overflow in history already!? inconceivable! Bad exchange! */
     381           0 :     GNUNET_break_op (0);
     382           0 :     return GNUNET_SYSERR;
     383             :   }
     384           0 :   return GNUNET_OK;
     385             : }
     386             : 
     387             : 
     388             : /**
     389             :  * Parse "merge" reserve history entry.
     390             :  *
     391             :  * @param[in,out] rh entry to parse
     392             :  * @param uc our context
     393             :  * @param transaction the transaction to parse
     394             :  * @return #GNUNET_OK on success
     395             :  */
     396             : static enum GNUNET_GenericReturnValue
     397           0 : parse_merge (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
     398             :              struct HistoryParseContext *uc,
     399             :              const json_t *transaction)
     400             : {
     401             :   uint32_t flags32;
     402             :   struct GNUNET_JSON_Specification merge_spec[] = {
     403           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     404             :                                  &rh->details.merge_details.h_contract_terms),
     405           0 :     GNUNET_JSON_spec_fixed_auto ("merge_pub",
     406             :                                  &rh->details.merge_details.merge_pub),
     407           0 :     GNUNET_JSON_spec_fixed_auto ("purse_pub",
     408             :                                  &rh->details.merge_details.purse_pub),
     409           0 :     GNUNET_JSON_spec_uint32 ("min_age",
     410             :                              &rh->details.merge_details.min_age),
     411           0 :     GNUNET_JSON_spec_uint32 ("flags",
     412             :                              &flags32),
     413           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     414             :                                  &rh->details.merge_details.reserve_sig),
     415           0 :     TALER_JSON_spec_amount_any ("purse_fee",
     416             :                                 &rh->details.merge_details.purse_fee),
     417           0 :     GNUNET_JSON_spec_timestamp ("merge_timestamp",
     418             :                                 &rh->details.merge_details.merge_timestamp),
     419           0 :     GNUNET_JSON_spec_timestamp ("purse_expiration",
     420             :                                 &rh->details.merge_details.purse_expiration),
     421           0 :     GNUNET_JSON_spec_bool ("merged",
     422             :                            &rh->details.merge_details.merged),
     423           0 :     GNUNET_JSON_spec_end ()
     424             :   };
     425             : 
     426           0 :   rh->type = TALER_EXCHANGE_RTT_MERGE;
     427           0 :   if (GNUNET_OK !=
     428           0 :       GNUNET_JSON_parse (transaction,
     429             :                          merge_spec,
     430             :                          NULL, NULL))
     431             :   {
     432           0 :     GNUNET_break_op (0);
     433           0 :     return GNUNET_SYSERR;
     434             :   }
     435           0 :   rh->details.merge_details.flags =
     436             :     (enum TALER_WalletAccountMergeFlags) flags32;
     437           0 :   if (GNUNET_OK !=
     438           0 :       TALER_wallet_account_merge_verify (
     439             :         rh->details.merge_details.merge_timestamp,
     440           0 :         &rh->details.merge_details.purse_pub,
     441             :         rh->details.merge_details.purse_expiration,
     442           0 :         &rh->details.merge_details.h_contract_terms,
     443           0 :         &rh->amount,
     444           0 :         &rh->details.merge_details.purse_fee,
     445             :         rh->details.merge_details.min_age,
     446             :         rh->details.merge_details.flags,
     447             :         uc->reserve_pub,
     448           0 :         &rh->details.merge_details.reserve_sig))
     449             :   {
     450           0 :     GNUNET_break_op (0);
     451           0 :     return GNUNET_SYSERR;
     452             :   }
     453           0 :   if (rh->details.merge_details.merged)
     454             :   {
     455           0 :     if (0 >
     456           0 :         TALER_amount_add (uc->total_in,
     457           0 :                           uc->total_in,
     458           0 :                           &rh->amount))
     459             :     {
     460             :       /* overflow in history already!? inconceivable! Bad exchange! */
     461           0 :       GNUNET_break_op (0);
     462           0 :       return GNUNET_SYSERR;
     463             :     }
     464             :   }
     465             :   else
     466             :   {
     467           0 :     if (0 >
     468           0 :         TALER_amount_add (uc->total_out,
     469           0 :                           uc->total_out,
     470           0 :                           &rh->details.merge_details.purse_fee))
     471             :     {
     472             :       /* overflow in history already!? inconceivable! Bad exchange! */
     473           0 :       GNUNET_break_op (0);
     474           0 :       return GNUNET_SYSERR;
     475             :     }
     476             :   }
     477           0 :   return GNUNET_OK;
     478             : }
     479             : 
     480             : 
     481             : /**
     482             :  * Parse "history" reserve history entry.
     483             :  *
     484             :  * @param[in,out] rh entry to parse
     485             :  * @param uc our context
     486             :  * @param transaction the transaction to parse
     487             :  * @return #GNUNET_OK on success
     488             :  */
     489             : static enum GNUNET_GenericReturnValue
     490           0 : parse_history (struct TALER_EXCHANGE_ReserveHistoryEntry *rh,
     491             :                struct HistoryParseContext *uc,
     492             :                const json_t *transaction)
     493             : {
     494             :   struct GNUNET_JSON_Specification history_spec[] = {
     495           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     496             :                                  &rh->details.history_details.reserve_sig),
     497           0 :     GNUNET_JSON_spec_timestamp ("request_timestamp",
     498             :                                 &rh->details.history_details.request_timestamp),
     499           0 :     GNUNET_JSON_spec_end ()
     500             :   };
     501             : 
     502           0 :   rh->type = TALER_EXCHANGE_RTT_HISTORY;
     503           0 :   if (GNUNET_OK !=
     504           0 :       GNUNET_JSON_parse (transaction,
     505             :                          history_spec,
     506             :                          NULL, NULL))
     507             :   {
     508           0 :     GNUNET_break_op (0);
     509           0 :     return GNUNET_SYSERR;
     510             :   }
     511           0 :   if (GNUNET_OK !=
     512           0 :       TALER_wallet_reserve_history_verify (
     513             :         rh->details.history_details.request_timestamp,
     514           0 :         &rh->amount,
     515             :         uc->reserve_pub,
     516           0 :         &rh->details.history_details.reserve_sig))
     517             :   {
     518           0 :     GNUNET_break_op (0);
     519           0 :     return GNUNET_SYSERR;
     520             :   }
     521           0 :   if (0 >
     522           0 :       TALER_amount_add (uc->total_out,
     523           0 :                         uc->total_out,
     524           0 :                         &rh->amount))
     525             :   {
     526             :     /* overflow in history already!? inconceivable! Bad exchange! */
     527           0 :     GNUNET_break_op (0);
     528           0 :     return GNUNET_SYSERR;
     529             :   }
     530           0 :   return GNUNET_OK;
     531             : }
     532             : 
     533             : 
     534             : enum GNUNET_GenericReturnValue
     535           0 : TALER_EXCHANGE_parse_reserve_history (
     536             :   struct TALER_EXCHANGE_Handle *exchange,
     537             :   const json_t *history,
     538             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     539             :   const char *currency,
     540             :   struct TALER_Amount *total_in,
     541             :   struct TALER_Amount *total_out,
     542             :   unsigned int history_length,
     543             :   struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory)
     544           0 : {
     545             :   const struct
     546             :   {
     547             :     const char *type;
     548             :     ParseHelper helper;
     549           0 :   } map[] = {
     550             :     { "CREDIT", &parse_credit },
     551             :     { "WITHDRAW", &parse_withdraw },
     552             :     { "RECOUP", &parse_recoup },
     553             :     { "MERGE", &parse_merge },
     554             :     { "CLOSING", &parse_closing },
     555             :     { "HISTORY", &parse_history },
     556             :     { NULL, NULL }
     557             :   };
     558           0 :   struct GNUNET_HashCode uuid[history_length];
     559           0 :   struct HistoryParseContext uc = {
     560             :     .exchange = exchange,
     561             :     .reserve_pub = reserve_pub,
     562             :     .uuids = uuid,
     563             :     .total_in = total_in,
     564             :     .total_out = total_out
     565             :   };
     566             : 
     567           0 :   GNUNET_assert (GNUNET_OK ==
     568             :                  TALER_amount_set_zero (currency,
     569             :                                         total_in));
     570           0 :   GNUNET_assert (GNUNET_OK ==
     571             :                  TALER_amount_set_zero (currency,
     572             :                                         total_out));
     573           0 :   for (unsigned int off = 0; off<history_length; off++)
     574             :   {
     575           0 :     struct TALER_EXCHANGE_ReserveHistoryEntry *rh = &rhistory[off];
     576             :     json_t *transaction;
     577             :     struct TALER_Amount amount;
     578             :     const char *type;
     579             :     struct GNUNET_JSON_Specification hist_spec[] = {
     580           0 :       GNUNET_JSON_spec_string ("type",
     581             :                                &type),
     582           0 :       TALER_JSON_spec_amount_any ("amount",
     583             :                                   &amount),
     584             :       /* 'wire' and 'signature' are optional depending on 'type'! */
     585           0 :       GNUNET_JSON_spec_end ()
     586             :     };
     587           0 :     bool found = false;
     588             : 
     589           0 :     transaction = json_array_get (history,
     590             :                                   off);
     591           0 :     if (GNUNET_OK !=
     592           0 :         GNUNET_JSON_parse (transaction,
     593             :                            hist_spec,
     594             :                            NULL, NULL))
     595             :     {
     596           0 :       GNUNET_break_op (0);
     597           0 :       json_dumpf (transaction,
     598             :                   stderr,
     599             :                   JSON_INDENT (2));
     600           0 :       return GNUNET_SYSERR;
     601             :     }
     602           0 :     rh->amount = amount;
     603           0 :     if (GNUNET_YES !=
     604           0 :         TALER_amount_cmp_currency (&amount,
     605             :                                    total_in))
     606             :     {
     607           0 :       GNUNET_break_op (0);
     608           0 :       return GNUNET_SYSERR;
     609             :     }
     610           0 :     for (unsigned int i = 0; NULL != map[i].type; i++)
     611             :     {
     612           0 :       if (0 == strcasecmp (map[i].type,
     613             :                            type))
     614             :       {
     615           0 :         found = true;
     616           0 :         if (GNUNET_OK !=
     617           0 :             map[i].helper (rh,
     618             :                            &uc,
     619             :                            transaction))
     620             :         {
     621           0 :           GNUNET_break_op (0);
     622           0 :           return GNUNET_SYSERR;
     623             :         }
     624           0 :         break;
     625             :       }
     626             :     }
     627           0 :     if (! found)
     628             :     {
     629             :       /* unexpected 'type', protocol incompatibility, complain! */
     630           0 :       GNUNET_break_op (0);
     631           0 :       return GNUNET_SYSERR;
     632             :     }
     633             :   }
     634           0 :   return GNUNET_OK;
     635             : }
     636             : 
     637             : 
     638             : void
     639           0 : TALER_EXCHANGE_free_reserve_history (
     640             :   struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory,
     641             :   unsigned int len)
     642             : {
     643           0 :   for (unsigned int i = 0; i<len; i++)
     644             :   {
     645           0 :     switch (rhistory[i].type)
     646             :     {
     647           0 :     case TALER_EXCHANGE_RTT_CREDIT:
     648           0 :       GNUNET_free (rhistory[i].details.in_details.sender_url);
     649           0 :       break;
     650           0 :     case TALER_EXCHANGE_RTT_WITHDRAWAL:
     651           0 :       break;
     652           0 :     case TALER_EXCHANGE_RTT_RECOUP:
     653           0 :       break;
     654           0 :     case TALER_EXCHANGE_RTT_CLOSE:
     655           0 :       break;
     656           0 :     case TALER_EXCHANGE_RTT_HISTORY:
     657           0 :       break;
     658           0 :     case TALER_EXCHANGE_RTT_MERGE:
     659           0 :       break;
     660             :     }
     661           0 :   }
     662           0 :   GNUNET_free (rhistory);
     663           0 : }
     664             : 
     665             : 
     666             : /**
     667             :  * Context for coin helpers.
     668             :  */
     669             : struct CoinHistoryParseContext
     670             : {
     671             : 
     672             :   /**
     673             :    * Denomination of the coin.
     674             :    */
     675             :   const struct TALER_EXCHANGE_DenomPublicKey *dk;
     676             : 
     677             :   /**
     678             :    * Our coin public key.
     679             :    */
     680             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
     681             : 
     682             :   /**
     683             :    * Where to sum up total refunds.
     684             :    */
     685             :   struct TALER_Amount rtotal;
     686             : 
     687             :   /**
     688             :    * Total amount encountered.
     689             :    */
     690             :   struct TALER_Amount *total;
     691             : 
     692             : };
     693             : 
     694             : 
     695             : /**
     696             :  * Signature of functions that operate on one of
     697             :  * the coin's history entries.
     698             :  *
     699             :  * @param[in,out] pc overall context
     700             :  * @param amount main amount of this operation
     701             :  * @param transaction JSON details for the operation
     702             :  * @return #GNUNET_SYSERR on error,
     703             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     704             :  */
     705             : typedef enum GNUNET_GenericReturnValue
     706             : (*CoinCheckHelper)(struct CoinHistoryParseContext *pc,
     707             :                    const struct TALER_Amount *amount,
     708             :                    json_t *transaction);
     709             : 
     710             : 
     711             : /**
     712             :  * Handle deposit entry in the coin's history.
     713             :  *
     714             :  * @param[in,out] pc overall context
     715             :  * @param amount main amount of this operation
     716             :  * @param transaction JSON details for the operation
     717             :  * @return #GNUNET_SYSERR on error,
     718             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     719             :  */
     720             : static enum GNUNET_GenericReturnValue
     721           0 : help_deposit (struct CoinHistoryParseContext *pc,
     722             :               const struct TALER_Amount *amount,
     723             :               json_t *transaction)
     724             : {
     725             :   struct TALER_MerchantWireHashP h_wire;
     726             :   struct TALER_PrivateContractHashP h_contract_terms;
     727             :   // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
     728             :   struct GNUNET_TIME_Timestamp wallet_timestamp;
     729             :   struct TALER_MerchantPublicKeyP merchant_pub;
     730           0 :   struct GNUNET_TIME_Timestamp refund_deadline = {0};
     731             :   struct TALER_CoinSpendSignatureP sig;
     732             :   struct TALER_AgeCommitmentHash hac;
     733             :   bool no_hac;
     734             :   struct TALER_Amount deposit_fee;
     735             :   struct GNUNET_JSON_Specification spec[] = {
     736           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     737             :                                  &sig),
     738           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     739             :                                  &h_contract_terms),
     740           0 :     GNUNET_JSON_spec_fixed_auto ("h_wire",
     741             :                                  &h_wire),
     742           0 :     GNUNET_JSON_spec_mark_optional (
     743             :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     744             :                                    &hac),
     745             :       &no_hac),
     746           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     747             :                                 &wallet_timestamp),
     748           0 :     GNUNET_JSON_spec_mark_optional (
     749             :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     750             :                                   &refund_deadline),
     751             :       NULL),
     752           0 :     TALER_JSON_spec_amount_any ("deposit_fee",
     753             :                                 &deposit_fee),
     754           0 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     755             :                                  &merchant_pub),
     756           0 :     GNUNET_JSON_spec_end ()
     757             :   };
     758             : 
     759           0 :   if (GNUNET_OK !=
     760           0 :       GNUNET_JSON_parse (transaction,
     761             :                          spec,
     762             :                          NULL, NULL))
     763             :   {
     764           0 :     GNUNET_break_op (0);
     765           0 :     return GNUNET_SYSERR;
     766             :   }
     767           0 :   if (GNUNET_OK !=
     768           0 :       TALER_wallet_deposit_verify (
     769             :         amount,
     770             :         &deposit_fee,
     771             :         &h_wire,
     772             :         &h_contract_terms,
     773             :         no_hac ? NULL : &hac,
     774             :         NULL /* h_extensions! */,
     775           0 :         &pc->dk->h_key,
     776             :         wallet_timestamp,
     777             :         &merchant_pub,
     778             :         refund_deadline,
     779             :         pc->coin_pub,
     780             :         &sig))
     781             :   {
     782           0 :     GNUNET_break_op (0);
     783           0 :     return GNUNET_SYSERR;
     784             :   }
     785           0 :   if (NULL != pc->dk)
     786             :   {
     787             :     /* check that deposit fee matches our expectations from /keys! */
     788           0 :     if ( (GNUNET_YES !=
     789           0 :           TALER_amount_cmp_currency (&deposit_fee,
     790           0 :                                      &pc->dk->fees.deposit)) ||
     791             :          (0 !=
     792           0 :           TALER_amount_cmp (&deposit_fee,
     793           0 :                             &pc->dk->fees.deposit)) )
     794             :     {
     795           0 :       GNUNET_break_op (0);
     796           0 :       return GNUNET_SYSERR;
     797             :     }
     798             :   }
     799           0 :   return GNUNET_YES;
     800             : }
     801             : 
     802             : 
     803             : /**
     804             :  * Handle melt entry in the coin's history.
     805             :  *
     806             :  * @param[in,out] pc overall context
     807             :  * @param amount main amount of this operation
     808             :  * @param transaction JSON details for the operation
     809             :  * @return #GNUNET_SYSERR on error,
     810             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     811             :  */
     812             : static enum GNUNET_GenericReturnValue
     813           0 : help_melt (struct CoinHistoryParseContext *pc,
     814             :            const struct TALER_Amount *amount,
     815             :            json_t *transaction)
     816             : {
     817             :   struct TALER_CoinSpendSignatureP sig;
     818             :   struct TALER_RefreshCommitmentP rc;
     819             :   struct TALER_AgeCommitmentHash h_age_commitment;
     820             :   bool no_hac;
     821             :   struct TALER_Amount melt_fee;
     822             :   struct GNUNET_JSON_Specification spec[] = {
     823           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     824             :                                  &sig),
     825           0 :     GNUNET_JSON_spec_fixed_auto ("rc",
     826             :                                  &rc),
     827           0 :     GNUNET_JSON_spec_mark_optional (
     828             :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     829             :                                    &h_age_commitment),
     830             :       &no_hac),
     831           0 :     TALER_JSON_spec_amount_any ("melt_fee",
     832             :                                 &melt_fee),
     833           0 :     GNUNET_JSON_spec_end ()
     834             :   };
     835             : 
     836           0 :   if (GNUNET_OK !=
     837           0 :       GNUNET_JSON_parse (transaction,
     838             :                          spec,
     839             :                          NULL, NULL))
     840             :   {
     841           0 :     GNUNET_break_op (0);
     842           0 :     return GNUNET_SYSERR;
     843             :   }
     844             : 
     845             :   /* check that melt fee matches our expectations from /keys! */
     846           0 :   if ( (GNUNET_YES !=
     847           0 :         TALER_amount_cmp_currency (&melt_fee,
     848           0 :                                    &pc->dk->fees.refresh)) ||
     849             :        (0 !=
     850           0 :         TALER_amount_cmp (&melt_fee,
     851           0 :                           &pc->dk->fees.refresh)) )
     852             :   {
     853           0 :     GNUNET_break_op (0);
     854           0 :     return GNUNET_SYSERR;
     855             :   }
     856           0 :   if (GNUNET_OK !=
     857           0 :       TALER_wallet_melt_verify (
     858             :         amount,
     859             :         &melt_fee,
     860             :         &rc,
     861           0 :         &pc->dk->h_key,
     862             :         no_hac
     863             :         ? NULL
     864             :         : &h_age_commitment,
     865             :         pc->coin_pub,
     866             :         &sig))
     867             :   {
     868           0 :     GNUNET_break_op (0);
     869           0 :     return GNUNET_SYSERR;
     870             :   }
     871           0 :   return GNUNET_YES;
     872             : }
     873             : 
     874             : 
     875             : /**
     876             :  * Handle refund entry in the coin's history.
     877             :  *
     878             :  * @param[in,out] pc overall context
     879             :  * @param amount main amount of this operation
     880             :  * @param transaction JSON details for the operation
     881             :  * @return #GNUNET_SYSERR on error,
     882             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     883             :  */
     884             : static enum GNUNET_GenericReturnValue
     885           0 : help_refund (struct CoinHistoryParseContext *pc,
     886             :              const struct TALER_Amount *amount,
     887             :              json_t *transaction)
     888             : {
     889             :   struct TALER_PrivateContractHashP h_contract_terms;
     890             :   struct TALER_MerchantPublicKeyP merchant_pub;
     891             :   struct TALER_MerchantSignatureP sig;
     892             :   struct TALER_Amount refund_fee;
     893             :   struct TALER_Amount sig_amount;
     894             :   uint64_t rtransaction_id;
     895             :   struct GNUNET_JSON_Specification spec[] = {
     896           0 :     TALER_JSON_spec_amount_any ("refund_fee",
     897             :                                 &refund_fee),
     898           0 :     GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     899             :                                  &sig),
     900           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     901             :                                  &h_contract_terms),
     902           0 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     903             :                                  &merchant_pub),
     904           0 :     GNUNET_JSON_spec_uint64 ("rtransaction_id",
     905             :                              &rtransaction_id),
     906           0 :     GNUNET_JSON_spec_end ()
     907             :   };
     908             : 
     909           0 :   if (GNUNET_OK !=
     910           0 :       GNUNET_JSON_parse (transaction,
     911             :                          spec,
     912             :                          NULL, NULL))
     913             :   {
     914           0 :     GNUNET_break_op (0);
     915           0 :     return GNUNET_SYSERR;
     916             :   }
     917           0 :   if (0 >
     918           0 :       TALER_amount_add (&sig_amount,
     919             :                         &refund_fee,
     920             :                         amount))
     921             :   {
     922           0 :     GNUNET_break_op (0);
     923           0 :     return GNUNET_SYSERR;
     924             :   }
     925           0 :   if (GNUNET_OK !=
     926           0 :       TALER_merchant_refund_verify (pc->coin_pub,
     927             :                                     &h_contract_terms,
     928             :                                     rtransaction_id,
     929             :                                     &sig_amount,
     930             :                                     &merchant_pub,
     931             :                                     &sig))
     932             :   {
     933           0 :     GNUNET_break_op (0);
     934           0 :     return GNUNET_SYSERR;
     935             :   }
     936             :   /* NOTE: theoretically, we could also check that the given
     937             :      merchant_pub and h_contract_terms appear in the
     938             :      history under deposits.  However, there is really no benefit
     939             :      for the exchange to lie here, so not checking is probably OK
     940             :      (an auditor ought to check, though). Then again, we similarly
     941             :      had no reason to check the merchant's signature (other than a
     942             :      well-formendess check). */
     943             : 
     944             :   /* check that refund fee matches our expectations from /keys! */
     945           0 :   if ( (GNUNET_YES !=
     946           0 :         TALER_amount_cmp_currency (&refund_fee,
     947           0 :                                    &pc->dk->fees.refund)) ||
     948             :        (0 !=
     949           0 :         TALER_amount_cmp (&refund_fee,
     950           0 :                           &pc->dk->fees.refund)) )
     951             :   {
     952           0 :     GNUNET_break_op (0);
     953           0 :     return GNUNET_SYSERR;
     954             :   }
     955           0 :   return GNUNET_NO;
     956             : }
     957             : 
     958             : 
     959             : /**
     960             :  * Handle recoup entry in the coin's history.
     961             :  *
     962             :  * @param[in,out] pc overall context
     963             :  * @param amount main amount of this operation
     964             :  * @param transaction JSON details for the operation
     965             :  * @return #GNUNET_SYSERR on error,
     966             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
     967             :  */
     968             : static enum GNUNET_GenericReturnValue
     969           0 : help_recoup (struct CoinHistoryParseContext *pc,
     970             :              const struct TALER_Amount *amount,
     971             :              json_t *transaction)
     972             : {
     973             :   struct TALER_ReservePublicKeyP reserve_pub;
     974             :   struct GNUNET_TIME_Timestamp timestamp;
     975             :   union TALER_DenominationBlindingKeyP coin_bks;
     976             :   struct TALER_ExchangePublicKeyP exchange_pub;
     977             :   struct TALER_ExchangeSignatureP exchange_sig;
     978             :   struct TALER_CoinSpendSignatureP coin_sig;
     979             :   struct GNUNET_JSON_Specification spec[] = {
     980           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     981             :                                  &exchange_sig),
     982           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     983             :                                  &exchange_pub),
     984           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
     985             :                                  &reserve_pub),
     986           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     987             :                                  &coin_sig),
     988           0 :     GNUNET_JSON_spec_fixed_auto ("coin_blind",
     989             :                                  &coin_bks),
     990           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     991             :                                 &timestamp),
     992           0 :     GNUNET_JSON_spec_end ()
     993             :   };
     994             : 
     995           0 :   if (GNUNET_OK !=
     996           0 :       GNUNET_JSON_parse (transaction,
     997             :                          spec,
     998             :                          NULL, NULL))
     999             :   {
    1000           0 :     GNUNET_break_op (0);
    1001           0 :     return GNUNET_SYSERR;
    1002             :   }
    1003           0 :   if (GNUNET_OK !=
    1004           0 :       TALER_exchange_online_confirm_recoup_verify (
    1005             :         timestamp,
    1006             :         amount,
    1007             :         pc->coin_pub,
    1008             :         &reserve_pub,
    1009             :         &exchange_pub,
    1010             :         &exchange_sig))
    1011             :   {
    1012           0 :     GNUNET_break_op (0);
    1013           0 :     return GNUNET_SYSERR;
    1014             :   }
    1015           0 :   if (GNUNET_OK !=
    1016           0 :       TALER_wallet_recoup_verify (&pc->dk->h_key,
    1017             :                                   &coin_bks,
    1018             :                                   pc->coin_pub,
    1019             :                                   &coin_sig))
    1020             :   {
    1021           0 :     GNUNET_break_op (0);
    1022           0 :     return GNUNET_SYSERR;
    1023             :   }
    1024           0 :   return GNUNET_YES;
    1025             : }
    1026             : 
    1027             : 
    1028             : /**
    1029             :  * Handle recoup-refresh entry in the coin's history.
    1030             :  *
    1031             :  * @param[in,out] pc overall context
    1032             :  * @param amount main amount of this operation
    1033             :  * @param transaction JSON details for the operation
    1034             :  * @return #GNUNET_SYSERR on error,
    1035             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
    1036             :  */
    1037             : static enum GNUNET_GenericReturnValue
    1038           0 : help_recoup_refresh (struct CoinHistoryParseContext *pc,
    1039             :                      const struct TALER_Amount *amount,
    1040             :                      json_t *transaction)
    1041             : {
    1042             :   /* This is the coin that was subjected to a recoup,
    1043             :        the value being credited to the old coin. */
    1044             :   struct TALER_CoinSpendPublicKeyP old_coin_pub;
    1045             :   union TALER_DenominationBlindingKeyP coin_bks;
    1046             :   struct GNUNET_TIME_Timestamp timestamp;
    1047             :   struct TALER_ExchangePublicKeyP exchange_pub;
    1048             :   struct TALER_ExchangeSignatureP exchange_sig;
    1049             :   struct TALER_CoinSpendSignatureP coin_sig;
    1050             :   struct GNUNET_JSON_Specification spec[] = {
    1051           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    1052             :                                  &exchange_sig),
    1053           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    1054             :                                  &exchange_pub),
    1055           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1056             :                                  &coin_sig),
    1057           0 :     GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
    1058             :                                  &old_coin_pub),
    1059           0 :     GNUNET_JSON_spec_fixed_auto ("coin_blind",
    1060             :                                  &coin_bks),
    1061           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
    1062             :                                 &timestamp),
    1063           0 :     GNUNET_JSON_spec_end ()
    1064             :   };
    1065             : 
    1066           0 :   if (GNUNET_OK !=
    1067           0 :       GNUNET_JSON_parse (transaction,
    1068             :                          spec,
    1069             :                          NULL, NULL))
    1070             :   {
    1071           0 :     GNUNET_break_op (0);
    1072           0 :     return GNUNET_SYSERR;
    1073             :   }
    1074           0 :   if (GNUNET_OK !=
    1075           0 :       TALER_exchange_online_confirm_recoup_refresh_verify (
    1076             :         timestamp,
    1077             :         amount,
    1078             :         pc->coin_pub,
    1079             :         &old_coin_pub,
    1080             :         &exchange_pub,
    1081             :         &exchange_sig))
    1082             :   {
    1083           0 :     GNUNET_break_op (0);
    1084           0 :     return GNUNET_SYSERR;
    1085             :   }
    1086           0 :   if (GNUNET_OK !=
    1087           0 :       TALER_wallet_recoup_verify (&pc->dk->h_key,
    1088             :                                   &coin_bks,
    1089             :                                   pc->coin_pub,
    1090             :                                   &coin_sig))
    1091             :   {
    1092           0 :     GNUNET_break_op (0);
    1093           0 :     return GNUNET_SYSERR;
    1094             :   }
    1095           0 :   return GNUNET_YES;
    1096             : }
    1097             : 
    1098             : 
    1099             : /**
    1100             :  * Handle old coin recoup entry in the coin's history.
    1101             :  *
    1102             :  * @param[in,out] pc overall context
    1103             :  * @param amount main amount of this operation
    1104             :  * @param transaction JSON details for the operation
    1105             :  * @return #GNUNET_SYSERR on error,
    1106             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
    1107             :  */
    1108             : static enum GNUNET_GenericReturnValue
    1109           0 : help_old_coin_recoup (struct CoinHistoryParseContext *pc,
    1110             :                       const struct TALER_Amount *amount,
    1111             :                       json_t *transaction)
    1112             : {
    1113             :   /* This is the coin that was credited in a recoup,
    1114             :        the value being credited to the this coin. */
    1115             :   struct TALER_ExchangePublicKeyP exchange_pub;
    1116             :   struct TALER_ExchangeSignatureP exchange_sig;
    1117             :   struct TALER_CoinSpendPublicKeyP new_coin_pub;
    1118             :   struct GNUNET_TIME_Timestamp timestamp;
    1119             :   struct GNUNET_JSON_Specification spec[] = {
    1120           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    1121             :                                  &exchange_sig),
    1122           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    1123             :                                  &exchange_pub),
    1124           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
    1125             :                                  &new_coin_pub),
    1126           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
    1127             :                                 &timestamp),
    1128           0 :     GNUNET_JSON_spec_end ()
    1129             :   };
    1130             : 
    1131           0 :   if (GNUNET_OK !=
    1132           0 :       GNUNET_JSON_parse (transaction,
    1133             :                          spec,
    1134             :                          NULL, NULL))
    1135             :   {
    1136           0 :     GNUNET_break_op (0);
    1137           0 :     return GNUNET_SYSERR;
    1138             :   }
    1139           0 :   if (GNUNET_OK !=
    1140           0 :       TALER_exchange_online_confirm_recoup_refresh_verify (
    1141             :         timestamp,
    1142             :         amount,
    1143             :         &new_coin_pub,
    1144             :         pc->coin_pub,
    1145             :         &exchange_pub,
    1146             :         &exchange_sig))
    1147             :   {
    1148           0 :     GNUNET_break_op (0);
    1149           0 :     return GNUNET_SYSERR;
    1150             :   }
    1151           0 :   return GNUNET_NO;
    1152             : }
    1153             : 
    1154             : 
    1155             : /**
    1156             :  * Handle purse deposit entry in the coin's history.
    1157             :  *
    1158             :  * @param[in,out] pc overall context
    1159             :  * @param amount main amount of this operation
    1160             :  * @param transaction JSON details for the operation
    1161             :  * @return #GNUNET_SYSERR on error,
    1162             :  *         #GNUNET_OK to add, #GNUNET_NO to subtract
    1163             :  */
    1164             : static enum GNUNET_GenericReturnValue
    1165           0 : help_purse_deposit (struct CoinHistoryParseContext *pc,
    1166             :                     const struct TALER_Amount *amount,
    1167             :                     json_t *transaction)
    1168             : {
    1169             :   struct TALER_PurseContractPublicKeyP purse_pub;
    1170             :   struct TALER_CoinSpendSignatureP coin_sig;
    1171             :   const char *exchange_base_url;
    1172             :   bool refunded;
    1173           0 :   struct TALER_AgeCommitmentHash phac = { 0 };
    1174             :   struct GNUNET_JSON_Specification spec[] = {
    1175           0 :     GNUNET_JSON_spec_fixed_auto ("purse_pub",
    1176             :                                  &purse_pub),
    1177           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1178             :                                  &coin_sig),
    1179           0 :     GNUNET_JSON_spec_mark_optional (
    1180             :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    1181             :                                    &coin_sig),
    1182             :       NULL),
    1183           0 :     GNUNET_JSON_spec_string ("exchange_base_url",
    1184             :                              &exchange_base_url),
    1185           0 :     GNUNET_JSON_spec_mark_optional (
    1186             :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    1187             :                                    &phac),
    1188             :       NULL),
    1189           0 :     GNUNET_JSON_spec_bool ("refunded",
    1190             :                            &refunded),
    1191           0 :     GNUNET_JSON_spec_end ()
    1192             :   };
    1193             : 
    1194           0 :   if (GNUNET_OK !=
    1195           0 :       GNUNET_JSON_parse (transaction,
    1196             :                          spec,
    1197             :                          NULL, NULL))
    1198             :   {
    1199           0 :     GNUNET_break_op (0);
    1200           0 :     return GNUNET_SYSERR;
    1201             :   }
    1202           0 :   if (GNUNET_OK !=
    1203           0 :       TALER_wallet_purse_deposit_verify (
    1204             :         exchange_base_url,
    1205             :         &purse_pub,
    1206             :         amount,
    1207           0 :         &pc->dk->h_key,
    1208             :         &phac,
    1209             :         pc->coin_pub,
    1210             :         &coin_sig))
    1211             :   {
    1212           0 :     GNUNET_break_op (0);
    1213           0 :     return GNUNET_SYSERR;
    1214             :   }
    1215           0 :   if (refunded)
    1216             :   {
    1217             :     /* We add the amount to refunds here, the original
    1218             :        deposit will be added to the balance later because
    1219             :        we still return GNUNET_YES, thus effectively
    1220             :        cancelling out this operation with respect to
    1221             :        the final balance. */
    1222           0 :     if (0 >
    1223           0 :         TALER_amount_add (&pc->rtotal,
    1224           0 :                           &pc->rtotal,
    1225             :                           amount))
    1226             :     {
    1227             :       /* overflow in refund history? inconceivable! Bad exchange! */
    1228           0 :       GNUNET_break_op (0);
    1229           0 :       return GNUNET_SYSERR;
    1230             :     }
    1231             :   }
    1232           0 :   return GNUNET_YES;
    1233             : }
    1234             : 
    1235             : 
    1236             : enum GNUNET_GenericReturnValue
    1237           0 : TALER_EXCHANGE_verify_coin_history (
    1238             :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
    1239             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1240             :   json_t *history,
    1241             :   struct TALER_Amount *total)
    1242             : {
    1243           0 :   const char *currency = dk->value.currency;
    1244             :   const struct
    1245             :   {
    1246             :     const char *type;
    1247             :     CoinCheckHelper helper;
    1248           0 :   } map[] = {
    1249             :     { "DEPOSIT", &help_deposit },
    1250             :     { "MELT", &help_melt },
    1251             :     { "REFUND", &help_refund },
    1252             :     { "RECOUP", &help_recoup },
    1253             :     { "RECOUP-REFRESH", &help_recoup_refresh },
    1254             :     { "OLD-COIN-RECOUP", &help_old_coin_recoup },
    1255             :     { "PURSE-DEPOSIT", &help_purse_deposit },
    1256             :     { NULL, NULL }
    1257             :   };
    1258           0 :   struct CoinHistoryParseContext pc = {
    1259             :     .dk = dk,
    1260             :     .coin_pub = coin_pub,
    1261             :     .total = total
    1262             :   };
    1263             :   size_t len;
    1264             : 
    1265           0 :   if (NULL == history)
    1266             :   {
    1267           0 :     GNUNET_break_op (0);
    1268           0 :     return GNUNET_SYSERR;
    1269             :   }
    1270           0 :   len = json_array_size (history);
    1271           0 :   if (0 == len)
    1272             :   {
    1273           0 :     GNUNET_break_op (0);
    1274           0 :     return GNUNET_SYSERR;
    1275             :   }
    1276           0 :   GNUNET_assert (GNUNET_OK ==
    1277             :                  TALER_amount_set_zero (currency,
    1278             :                                         total));
    1279           0 :   GNUNET_assert (GNUNET_OK ==
    1280             :                  TALER_amount_set_zero (currency,
    1281             :                                         &pc.rtotal));
    1282           0 :   for (size_t off = 0; off<len; off++)
    1283             :   {
    1284             :     enum GNUNET_GenericReturnValue add;
    1285             :     json_t *transaction;
    1286             :     struct TALER_Amount amount;
    1287             :     const char *type;
    1288             :     struct GNUNET_JSON_Specification spec_glob[] = {
    1289           0 :       TALER_JSON_spec_amount_any ("amount",
    1290             :                                   &amount),
    1291           0 :       GNUNET_JSON_spec_string ("type",
    1292             :                                &type),
    1293           0 :       GNUNET_JSON_spec_end ()
    1294             :     };
    1295             : 
    1296           0 :     transaction = json_array_get (history,
    1297             :                                   off);
    1298           0 :     if (GNUNET_OK !=
    1299           0 :         GNUNET_JSON_parse (transaction,
    1300             :                            spec_glob,
    1301             :                            NULL, NULL))
    1302             :     {
    1303           0 :       GNUNET_break_op (0);
    1304           0 :       return GNUNET_SYSERR;
    1305             :     }
    1306           0 :     if (GNUNET_YES !=
    1307           0 :         TALER_amount_cmp_currency (&amount,
    1308             :                                    &pc.rtotal))
    1309             :     {
    1310           0 :       GNUNET_break_op (0);
    1311           0 :       return GNUNET_SYSERR;
    1312             :     }
    1313           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1314             :                 "Operation of type %s with amount %s\n",
    1315             :                 type,
    1316             :                 TALER_amount2s (&amount));
    1317           0 :     add = GNUNET_SYSERR;
    1318           0 :     for (unsigned int i = 0; NULL != map[i].type; i++)
    1319             :     {
    1320           0 :       if (0 == strcasecmp (type,
    1321             :                            map[i].type))
    1322             :       {
    1323           0 :         add = map[i].helper (&pc,
    1324             :                              &amount,
    1325             :                              transaction);
    1326           0 :         break;
    1327             :       }
    1328             :     }
    1329           0 :     switch (add)
    1330             :     {
    1331           0 :     case GNUNET_SYSERR:
    1332             :       /* entry type not supported, new version on server? */
    1333           0 :       GNUNET_break_op (0);
    1334           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1335             :                   "Unexpected type `%s' in response\n",
    1336             :                   type);
    1337           0 :       return GNUNET_SYSERR;
    1338           0 :     case GNUNET_YES:
    1339             :       /* This amount should be added to the total */
    1340           0 :       if (0 >
    1341           0 :           TALER_amount_add (total,
    1342             :                             total,
    1343             :                             &amount))
    1344             :       {
    1345             :         /* overflow in history already!? inconceivable! Bad exchange! */
    1346           0 :         GNUNET_break_op (0);
    1347           0 :         return GNUNET_SYSERR;
    1348             :       }
    1349           0 :       break;
    1350           0 :     case GNUNET_NO:
    1351             :       /* This amount should be subtracted from the total.
    1352             : 
    1353             :          However, for the implementation, we first *add* up all of
    1354             :          these negative amounts, as we might get refunds before
    1355             :          deposits from a semi-evil exchange.  Then, at the end, we do
    1356             :          the subtraction by calculating "total = total - rtotal" */
    1357           0 :       if (0 >
    1358           0 :           TALER_amount_add (&pc.rtotal,
    1359             :                             &pc.rtotal,
    1360             :                             &amount))
    1361             :       {
    1362             :         /* overflow in refund history? inconceivable! Bad exchange! */
    1363           0 :         GNUNET_break_op (0);
    1364           0 :         return GNUNET_SYSERR;
    1365             :       }
    1366           0 :       break;
    1367             :     } /* end of switch(add) */
    1368           0 :   }
    1369             :   /* Finally, subtract 'rtotal' from total to handle the subtractions */
    1370           0 :   if (0 >
    1371           0 :       TALER_amount_subtract (total,
    1372             :                              total,
    1373             :                              &pc.rtotal))
    1374             :   {
    1375             :     /* underflow in history? inconceivable! Bad exchange! */
    1376           0 :     GNUNET_break_op (0);
    1377           0 :     return GNUNET_SYSERR;
    1378             :   }
    1379           0 :   return GNUNET_OK;
    1380             : }
    1381             : 
    1382             : 
    1383             : const struct TALER_EXCHANGE_SigningPublicKey *
    1384           0 : TALER_EXCHANGE_get_signing_key_info (
    1385             :   const struct TALER_EXCHANGE_Keys *keys,
    1386             :   const struct TALER_ExchangePublicKeyP *exchange_pub)
    1387             : {
    1388           0 :   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
    1389             :   {
    1390           0 :     const struct TALER_EXCHANGE_SigningPublicKey *spk
    1391           0 :       = &keys->sign_keys[i];
    1392             : 
    1393           0 :     if (0 == GNUNET_memcmp (exchange_pub,
    1394             :                             &spk->key))
    1395           0 :       return spk;
    1396             :   }
    1397           0 :   return NULL;
    1398             : }
    1399             : 
    1400             : 
    1401             : enum GNUNET_GenericReturnValue
    1402           0 : TALER_EXCHANGE_check_purse_create_conflict_ (
    1403             :   const struct TALER_PurseContractSignatureP *cpurse_sig,
    1404             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1405             :   const json_t *proof)
    1406             : {
    1407             :   struct TALER_Amount amount;
    1408             :   uint32_t min_age;
    1409             :   struct GNUNET_TIME_Timestamp purse_expiration;
    1410             :   struct TALER_PurseContractSignatureP purse_sig;
    1411             :   struct TALER_PrivateContractHashP h_contract_terms;
    1412             :   struct TALER_PurseMergePublicKeyP merge_pub;
    1413             :   struct GNUNET_JSON_Specification spec[] = {
    1414           0 :     TALER_JSON_spec_amount_any ("amount",
    1415             :                                 &amount),
    1416           0 :     GNUNET_JSON_spec_uint32 ("min_age",
    1417             :                              &min_age),
    1418           0 :     GNUNET_JSON_spec_timestamp ("purse_expiration",
    1419             :                                 &purse_expiration),
    1420           0 :     GNUNET_JSON_spec_fixed_auto ("purse_sig",
    1421             :                                  &purse_sig),
    1422           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
    1423             :                                  &h_contract_terms),
    1424           0 :     GNUNET_JSON_spec_fixed_auto ("merge_pub",
    1425             :                                  &merge_pub),
    1426           0 :     GNUNET_JSON_spec_end ()
    1427             :   };
    1428             : 
    1429           0 :   if (GNUNET_OK !=
    1430           0 :       GNUNET_JSON_parse (proof,
    1431             :                          spec,
    1432             :                          NULL, NULL))
    1433             :   {
    1434           0 :     GNUNET_break_op (0);
    1435           0 :     return GNUNET_SYSERR;
    1436             :   }
    1437           0 :   if (GNUNET_OK !=
    1438           0 :       TALER_wallet_purse_create_verify (purse_expiration,
    1439             :                                         &h_contract_terms,
    1440             :                                         &merge_pub,
    1441             :                                         min_age,
    1442             :                                         &amount,
    1443             :                                         purse_pub,
    1444             :                                         &purse_sig))
    1445             :   {
    1446           0 :     GNUNET_break_op (0);
    1447           0 :     return GNUNET_SYSERR;
    1448             :   }
    1449           0 :   if (0 ==
    1450           0 :       GNUNET_memcmp (&purse_sig,
    1451             :                      cpurse_sig))
    1452             :   {
    1453             :     /* Must be the SAME data, not a conflict! */
    1454           0 :     GNUNET_break_op (0);
    1455           0 :     return GNUNET_SYSERR;
    1456             :   }
    1457           0 :   return GNUNET_OK;
    1458             : }
    1459             : 
    1460             : 
    1461             : enum GNUNET_GenericReturnValue
    1462           0 : TALER_EXCHANGE_check_purse_merge_conflict_ (
    1463             :   const struct TALER_PurseMergeSignatureP *cmerge_sig,
    1464             :   const struct TALER_PurseMergePublicKeyP *merge_pub,
    1465             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1466             :   const char *exchange_url,
    1467             :   const json_t *proof)
    1468             : {
    1469             :   struct TALER_PurseMergeSignatureP merge_sig;
    1470             :   struct GNUNET_TIME_Timestamp merge_timestamp;
    1471           0 :   const char *partner_url = NULL;
    1472             :   struct TALER_ReservePublicKeyP reserve_pub;
    1473             :   struct GNUNET_JSON_Specification spec[] = {
    1474           0 :     GNUNET_JSON_spec_mark_optional (
    1475             :       GNUNET_JSON_spec_string ("partner_url",
    1476             :                                &partner_url),
    1477             :       NULL),
    1478           0 :     GNUNET_JSON_spec_timestamp ("merge_timestamp",
    1479             :                                 &merge_timestamp),
    1480           0 :     GNUNET_JSON_spec_fixed_auto ("merge_sig",
    1481             :                                  &merge_sig),
    1482           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
    1483             :                                  &reserve_pub),
    1484           0 :     GNUNET_JSON_spec_end ()
    1485             :   };
    1486             :   char *payto_uri;
    1487             : 
    1488           0 :   if (GNUNET_OK !=
    1489           0 :       GNUNET_JSON_parse (proof,
    1490             :                          spec,
    1491             :                          NULL, NULL))
    1492             :   {
    1493           0 :     GNUNET_break_op (0);
    1494           0 :     return GNUNET_SYSERR;
    1495             :   }
    1496           0 :   if (NULL == partner_url)
    1497           0 :     partner_url = exchange_url;
    1498           0 :   payto_uri = TALER_reserve_make_payto (partner_url,
    1499             :                                         &reserve_pub);
    1500           0 :   if (GNUNET_OK !=
    1501           0 :       TALER_wallet_purse_merge_verify (
    1502             :         payto_uri,
    1503             :         merge_timestamp,
    1504             :         purse_pub,
    1505             :         merge_pub,
    1506             :         &merge_sig))
    1507             :   {
    1508           0 :     GNUNET_break_op (0);
    1509           0 :     GNUNET_free (payto_uri);
    1510           0 :     return GNUNET_SYSERR;
    1511             :   }
    1512           0 :   GNUNET_free (payto_uri);
    1513           0 :   if (0 ==
    1514           0 :       GNUNET_memcmp (&merge_sig,
    1515             :                      cmerge_sig))
    1516             :   {
    1517             :     /* Must be the SAME data, not a conflict! */
    1518           0 :     GNUNET_break_op (0);
    1519           0 :     return GNUNET_SYSERR;
    1520             :   }
    1521           0 :   return GNUNET_OK;
    1522             : }
    1523             : 
    1524             : 
    1525             : enum GNUNET_GenericReturnValue
    1526           0 : TALER_EXCHANGE_check_purse_coin_conflict_ (
    1527             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1528             :   const char *exchange_url,
    1529             :   const json_t *proof,
    1530             :   struct TALER_DenominationHashP *h_denom_pub,
    1531             :   struct TALER_AgeCommitmentHash *phac,
    1532             :   struct TALER_CoinSpendPublicKeyP *coin_pub,
    1533             :   struct TALER_CoinSpendSignatureP *coin_sig)
    1534             : {
    1535           0 :   const char *partner_url = NULL;
    1536             :   struct TALER_Amount amount;
    1537             :   struct GNUNET_JSON_Specification spec[] = {
    1538           0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
    1539             :                                  h_denom_pub),
    1540           0 :     GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    1541             :                                  phac),
    1542           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1543             :                                  coin_sig),
    1544           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
    1545             :                                  coin_pub),
    1546           0 :     GNUNET_JSON_spec_mark_optional (
    1547             :       GNUNET_JSON_spec_string ("partner_url",
    1548             :                                &partner_url),
    1549             :       NULL),
    1550           0 :     TALER_JSON_spec_amount_any ("amount",
    1551             :                                 &amount),
    1552           0 :     GNUNET_JSON_spec_end ()
    1553             :   };
    1554             : 
    1555           0 :   if (GNUNET_OK !=
    1556           0 :       GNUNET_JSON_parse (proof,
    1557             :                          spec,
    1558             :                          NULL, NULL))
    1559             :   {
    1560           0 :     GNUNET_break_op (0);
    1561           0 :     return GNUNET_SYSERR;
    1562             :   }
    1563           0 :   if (NULL == partner_url)
    1564           0 :     partner_url = exchange_url;
    1565           0 :   if (GNUNET_OK !=
    1566           0 :       TALER_wallet_purse_deposit_verify (
    1567             :         partner_url,
    1568             :         purse_pub,
    1569             :         &amount,
    1570             :         h_denom_pub,
    1571             :         phac,
    1572             :         coin_pub,
    1573             :         coin_sig))
    1574             :   {
    1575           0 :     GNUNET_break_op (0);
    1576           0 :     return GNUNET_SYSERR;
    1577             :   }
    1578           0 :   return GNUNET_OK;
    1579             : }
    1580             : 
    1581             : 
    1582             : enum GNUNET_GenericReturnValue
    1583           0 : TALER_EXCHANGE_check_purse_econtract_conflict_ (
    1584             :   const struct TALER_PurseContractSignatureP *ccontract_sig,
    1585             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1586             :   const json_t *proof)
    1587             : {
    1588             :   struct TALER_ContractDiffiePublicP contract_pub;
    1589             :   struct TALER_PurseContractSignatureP contract_sig;
    1590             :   struct GNUNET_HashCode h_econtract;
    1591             :   struct GNUNET_JSON_Specification spec[] = {
    1592           0 :     GNUNET_JSON_spec_fixed_auto ("h_econtract",
    1593             :                                  &h_econtract),
    1594           0 :     GNUNET_JSON_spec_fixed_auto ("econtract_sig",
    1595             :                                  &contract_sig),
    1596           0 :     GNUNET_JSON_spec_fixed_auto ("contract_pub",
    1597             :                                  &contract_pub),
    1598           0 :     GNUNET_JSON_spec_end ()
    1599             :   };
    1600             : 
    1601           0 :   if (GNUNET_OK !=
    1602           0 :       GNUNET_JSON_parse (proof,
    1603             :                          spec,
    1604             :                          NULL, NULL))
    1605             :   {
    1606           0 :     GNUNET_break_op (0);
    1607           0 :     return GNUNET_SYSERR;
    1608             :   }
    1609           0 :   if (GNUNET_OK !=
    1610           0 :       TALER_wallet_econtract_upload_verify2 (
    1611             :         &h_econtract,
    1612             :         &contract_pub,
    1613             :         purse_pub,
    1614             :         &contract_sig))
    1615             :   {
    1616           0 :     GNUNET_break_op (0);
    1617           0 :     return GNUNET_SYSERR;
    1618             :   }
    1619           0 :   if (0 ==
    1620           0 :       GNUNET_memcmp (&contract_sig,
    1621             :                      ccontract_sig))
    1622             :   {
    1623             :     /* Must be the SAME data, not a conflict! */
    1624           0 :     GNUNET_break_op (0);
    1625           0 :     return GNUNET_SYSERR;
    1626             :   }
    1627           0 :   return GNUNET_OK;
    1628             : }
    1629             : 
    1630             : 
    1631             : enum GNUNET_GenericReturnValue
    1632           0 : TALER_EXCHANGE_check_coin_amount_conflict_ (
    1633             :   const struct TALER_EXCHANGE_Keys *keys,
    1634             :   const json_t *proof,
    1635             :   struct TALER_CoinSpendPublicKeyP *coin_pub,
    1636             :   struct TALER_Amount *remaining)
    1637             : {
    1638             :   json_t *history;
    1639             :   struct TALER_Amount total;
    1640             :   struct TALER_DenominationHashP h_denom_pub;
    1641             :   const struct TALER_EXCHANGE_DenomPublicKey *dki;
    1642             :   struct GNUNET_JSON_Specification spec[] = {
    1643           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
    1644             :                                  coin_pub),
    1645           0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
    1646             :                                  &h_denom_pub),
    1647           0 :     GNUNET_JSON_spec_json ("history",
    1648             :                            &history),
    1649           0 :     GNUNET_JSON_spec_end ()
    1650             :   };
    1651             : 
    1652           0 :   if (GNUNET_OK !=
    1653           0 :       GNUNET_JSON_parse (proof,
    1654             :                          spec,
    1655             :                          NULL, NULL))
    1656             :   {
    1657           0 :     GNUNET_break_op (0);
    1658           0 :     return GNUNET_SYSERR;
    1659             :   }
    1660           0 :   dki = TALER_EXCHANGE_get_denomination_key_by_hash (
    1661             :     keys,
    1662             :     &h_denom_pub);
    1663           0 :   if (NULL == dki)
    1664             :   {
    1665           0 :     GNUNET_break_op (0);
    1666           0 :     return GNUNET_SYSERR;
    1667             :   }
    1668           0 :   if (GNUNET_OK !=
    1669           0 :       TALER_EXCHANGE_verify_coin_history (dki,
    1670             :                                           coin_pub,
    1671             :                                           history,
    1672             :                                           &total))
    1673             :   {
    1674           0 :     GNUNET_break_op (0);
    1675           0 :     json_decref (history);
    1676           0 :     return GNUNET_SYSERR;
    1677             :   }
    1678           0 :   json_decref (history);
    1679           0 :   if (0 >
    1680           0 :       TALER_amount_subtract (remaining,
    1681             :                              &dki->value,
    1682             :                              &total))
    1683             :   {
    1684             :     /* Strange 'proof': coin was double-spent
    1685             :        before our transaction?! */
    1686           0 :     GNUNET_break_op (0);
    1687           0 :     return GNUNET_SYSERR;
    1688             :   }
    1689           0 :   return GNUNET_OK;
    1690             : }
    1691             : 
    1692             : 
    1693             : /**
    1694             :  * Verify that @a coin_sig does NOT appear in
    1695             :  * the history of @a proof and thus whatever transaction
    1696             :  * is authorized by @a coin_sig is a conflict with
    1697             :  * @a proof.
    1698             :  *
    1699             :  * @param proof a proof to check
    1700             :  * @param coin_sig signature that must not be in @a proof
    1701             :  * @return #GNUNET_OK if @a coin_sig is not in @a proof
    1702             :  */
    1703             : enum GNUNET_GenericReturnValue
    1704           0 : TALER_EXCHANGE_check_coin_signature_conflict_ (
    1705             :   const json_t *proof,
    1706             :   const struct TALER_CoinSpendSignatureP *coin_sig)
    1707             : {
    1708             :   json_t *history;
    1709             :   size_t off;
    1710             :   json_t *entry;
    1711             : 
    1712           0 :   history = json_object_get (proof,
    1713             :                              "history");
    1714           0 :   if (NULL == history)
    1715             :   {
    1716           0 :     GNUNET_break (0);
    1717           0 :     return GNUNET_SYSERR;
    1718             :   }
    1719           0 :   json_array_foreach (history, off, entry)
    1720             :   {
    1721             :     struct TALER_CoinSpendSignatureP cs;
    1722             :     struct GNUNET_JSON_Specification spec[] = {
    1723           0 :       GNUNET_JSON_spec_fixed_auto ("coin_sig",
    1724             :                                    &cs),
    1725           0 :       GNUNET_JSON_spec_end ()
    1726             :     };
    1727             : 
    1728           0 :     if (GNUNET_OK !=
    1729           0 :         GNUNET_JSON_parse (entry,
    1730             :                            spec,
    1731             :                            NULL, NULL))
    1732           0 :       continue; /* entry without coin signature */
    1733           0 :     if (0 ==
    1734           0 :         GNUNET_memcmp (&cs,
    1735             :                        coin_sig))
    1736             :     {
    1737           0 :       GNUNET_break_op (0);
    1738           0 :       return GNUNET_SYSERR;
    1739             :     }
    1740             :   }
    1741           0 :   return GNUNET_OK;
    1742             : }
    1743             : 
    1744             : 
    1745             : enum GNUNET_GenericReturnValue
    1746           0 : TALER_EXCHANGE_check_coin_denomination_conflict_ (
    1747             :   const json_t *proof,
    1748             :   const struct TALER_DenominationHashP *ch_denom_pub)
    1749             : {
    1750             :   struct TALER_DenominationHashP h_denom_pub;
    1751             :   struct GNUNET_JSON_Specification spec[] = {
    1752           0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
    1753             :                                  &h_denom_pub),
    1754           0 :     GNUNET_JSON_spec_end ()
    1755             :   };
    1756             : 
    1757           0 :   if (GNUNET_OK !=
    1758           0 :       GNUNET_JSON_parse (proof,
    1759             :                          spec,
    1760             :                          NULL, NULL))
    1761             :   {
    1762           0 :     GNUNET_break_op (0);
    1763           0 :     return GNUNET_SYSERR;
    1764             :   }
    1765           0 :   if (0 ==
    1766           0 :       GNUNET_memcmp (ch_denom_pub,
    1767             :                      &h_denom_pub))
    1768             :   {
    1769           0 :     GNUNET_break_op (0);
    1770           0 :     return GNUNET_OK;
    1771             :   }
    1772             :   /* indeed, proof with different denomination key provided */
    1773           0 :   return GNUNET_OK;
    1774             : }
    1775             : 
    1776             : 
    1777             : enum GNUNET_GenericReturnValue
    1778           0 : TALER_EXCHANGE_check_coin_conflict_ (
    1779             :   const struct TALER_EXCHANGE_Keys *keys,
    1780             :   const json_t *proof,
    1781             :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
    1782             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1783             :   const struct TALER_CoinSpendSignatureP *coin_sig,
    1784             :   const struct TALER_Amount *required)
    1785             : {
    1786             :   enum TALER_ErrorCode ec;
    1787             : 
    1788           0 :   ec = TALER_JSON_get_error_code (proof);
    1789           0 :   switch (ec)
    1790             :   {
    1791           0 :   case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
    1792             :     {
    1793             :       struct TALER_Amount left;
    1794             :       struct TALER_CoinSpendPublicKeyP pcoin_pub;
    1795             : 
    1796           0 :       if (GNUNET_OK !=
    1797           0 :           TALER_EXCHANGE_check_coin_amount_conflict_ (
    1798             :             keys,
    1799             :             proof,
    1800             :             &pcoin_pub,
    1801             :             &left))
    1802             :       {
    1803           0 :         GNUNET_break_op (0);
    1804           0 :         return GNUNET_SYSERR;
    1805             :       }
    1806           0 :       if (0 !=
    1807           0 :           GNUNET_memcmp (&pcoin_pub,
    1808             :                          coin_pub))
    1809             :       {
    1810             :         /* conflict is for a different coin! */
    1811           0 :         GNUNET_break_op (0);
    1812           0 :         return GNUNET_SYSERR;
    1813             :       }
    1814           0 :       if (-1 !=
    1815           0 :           TALER_amount_cmp (&left,
    1816             :                             required))
    1817             :       {
    1818             :         /* Balance was sufficient after all; recoup MAY have still been possible */
    1819           0 :         GNUNET_break_op (0);
    1820           0 :         return GNUNET_SYSERR;
    1821             :       }
    1822           0 :       if (GNUNET_OK !=
    1823           0 :           TALER_EXCHANGE_check_coin_signature_conflict_ (
    1824             :             proof,
    1825             :             coin_sig))
    1826             :       {
    1827             :         /* Not a conflicting transaction: ours is included! */
    1828           0 :         GNUNET_break_op (0);
    1829           0 :         return GNUNET_SYSERR;
    1830             :       }
    1831           0 :       break;
    1832             :     }
    1833           0 :   case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
    1834             :     {
    1835             :       struct TALER_Amount left;
    1836             :       struct TALER_CoinSpendPublicKeyP pcoin_pub;
    1837             : 
    1838           0 :       if (GNUNET_OK !=
    1839           0 :           TALER_EXCHANGE_check_coin_amount_conflict_ (
    1840             :             keys,
    1841             :             proof,
    1842             :             &pcoin_pub,
    1843             :             &left))
    1844             :       {
    1845           0 :         GNUNET_break_op (0);
    1846           0 :         return GNUNET_SYSERR;
    1847             :       }
    1848           0 :       if (0 !=
    1849           0 :           GNUNET_memcmp (&pcoin_pub,
    1850             :                          coin_pub))
    1851             :       {
    1852             :         /* conflict is for a different coin! */
    1853           0 :         GNUNET_break_op (0);
    1854           0 :         return GNUNET_SYSERR;
    1855             :       }
    1856           0 :       if (GNUNET_OK !=
    1857           0 :           TALER_EXCHANGE_check_coin_denomination_conflict_ (
    1858             :             proof,
    1859             :             &dk->h_key))
    1860             :       {
    1861             :         /* Eh, same denomination, hence no conflict */
    1862           0 :         GNUNET_break_op (0);
    1863           0 :         return GNUNET_SYSERR;
    1864             :       }
    1865           0 :       break;
    1866             :     }
    1867           0 :   default:
    1868           0 :     GNUNET_break_op (0);
    1869           0 :     return GNUNET_SYSERR;
    1870             :   }
    1871           0 :   return GNUNET_OK;
    1872             : }
    1873             : 
    1874             : 
    1875             : enum GNUNET_GenericReturnValue
    1876           0 : TALER_EXCHANGE_get_min_denomination_ (
    1877             :   const struct TALER_EXCHANGE_Keys *keys,
    1878             :   struct TALER_Amount *min)
    1879             : {
    1880           0 :   bool have_min = false;
    1881           0 :   for (unsigned int i = 0; i<keys->num_denom_keys; i++)
    1882             :   {
    1883           0 :     const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i];
    1884             : 
    1885           0 :     if (! have_min)
    1886             :     {
    1887           0 :       *min = dk->value;
    1888           0 :       have_min = true;
    1889           0 :       continue;
    1890             :     }
    1891           0 :     if (1 != TALER_amount_cmp (min,
    1892             :                                &dk->value))
    1893           0 :       continue;
    1894           0 :     *min = dk->value;
    1895             :   }
    1896           0 :   if (! have_min)
    1897             :   {
    1898           0 :     GNUNET_break (0);
    1899           0 :     return GNUNET_SYSERR;
    1900             :   }
    1901           0 :   return GNUNET_OK;
    1902             : }
    1903             : 
    1904             : 
    1905             : enum GNUNET_GenericReturnValue
    1906           0 : TALER_EXCHANGE_verify_deposit_signature_ (
    1907             :   const struct TALER_EXCHANGE_DepositContractDetail *dcd,
    1908             :   const struct TALER_ExtensionContractHashP *ech,
    1909             :   const struct TALER_MerchantWireHashP *h_wire,
    1910             :   const struct TALER_EXCHANGE_CoinDepositDetail *cdd,
    1911             :   const struct TALER_EXCHANGE_DenomPublicKey *dki)
    1912             : {
    1913           0 :   if (GNUNET_OK !=
    1914           0 :       TALER_wallet_deposit_verify (&cdd->amount,
    1915             :                                    &dki->fees.deposit,
    1916             :                                    h_wire,
    1917             :                                    &dcd->h_contract_terms,
    1918             :                                    &cdd->h_age_commitment,
    1919             :                                    ech,
    1920             :                                    &cdd->h_denom_pub,
    1921             :                                    dcd->timestamp,
    1922             :                                    &dcd->merchant_pub,
    1923             :                                    dcd->refund_deadline,
    1924             :                                    &cdd->coin_pub,
    1925             :                                    &cdd->coin_sig))
    1926             :   {
    1927           0 :     GNUNET_break_op (0);
    1928           0 :     TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n");
    1929           0 :     TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
    1930             :                      TALER_amount2s (&cdd->amount));
    1931           0 :     TALER_LOG_DEBUG ("... deposit_fee was %s\n",
    1932             :                      TALER_amount2s (&dki->fees.deposit));
    1933           0 :     return GNUNET_SYSERR;
    1934             :   }
    1935             : 
    1936             :   /* check coin signature */
    1937             :   {
    1938           0 :     struct TALER_CoinPublicInfo coin_info = {
    1939             :       .coin_pub = cdd->coin_pub,
    1940             :       .denom_pub_hash = cdd->h_denom_pub,
    1941             :       .denom_sig = cdd->denom_sig,
    1942             :       .h_age_commitment = cdd->h_age_commitment,
    1943             :     };
    1944             : 
    1945           0 :     if (GNUNET_YES !=
    1946           0 :         TALER_test_coin_valid (&coin_info,
    1947             :                                &dki->key))
    1948             :     {
    1949           0 :       GNUNET_break_op (0);
    1950           0 :       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
    1951           0 :       return GNUNET_SYSERR;
    1952             :     }
    1953             :   }
    1954             : 
    1955             :   /* Check coin does make a contribution */
    1956           0 :   if (0 < TALER_amount_cmp (&dki->fees.deposit,
    1957             :                             &cdd->amount))
    1958             :   {
    1959           0 :     GNUNET_break_op (0);
    1960           0 :     TALER_LOG_WARNING ("Deposit amount smaller than fee\n");
    1961           0 :     return GNUNET_SYSERR;
    1962             :   }
    1963           0 :   return GNUNET_OK;
    1964             : }
    1965             : 
    1966             : 
    1967             : /* end of exchange_api_common.c */

Generated by: LCOV version 1.14