LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_exchanges.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.3 % 417 293
Test Date: 2025-11-06 19:31:41 Functions: 100.0 % 23 23

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2014-2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU 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 <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file taler-merchant-httpd_exchanges.c
      18              :  * @brief logic this HTTPD keeps for each exchange we interact with
      19              :  * @author Marcello Stanisci
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <taler/taler_json_lib.h>
      24              : #include <taler/taler_dbevents.h>
      25              : #include "taler-merchant-httpd_exchanges.h"
      26              : #include "taler-merchant-httpd.h"
      27              : #include <regex.h>
      28              : 
      29              : /**
      30              :  * How often do we retry DB transactions with soft errors?
      31              :  */
      32              : #define MAX_RETRIES 3
      33              : 
      34              : /**
      35              :  * Threshold after which exponential backoff should not increase.
      36              :  */
      37              : #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \
      38              :           GNUNET_TIME_UNIT_SECONDS, 60)
      39              : 
      40              : /**
      41              :  * This is how long /keys long-polls for, so we should
      42              :  * allow this time between requests if there is no
      43              :  * answer. See exchange_api_handle.c.
      44              :  */
      45              : #define LONG_POLL_THRESHOLD GNUNET_TIME_relative_multiply ( \
      46              :           GNUNET_TIME_UNIT_SECONDS, 120)
      47              : 
      48              : 
      49              : /**
      50              :  * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation.
      51              :  */
      52              : struct TMH_EXCHANGES_KeysOperation
      53              : {
      54              : 
      55              :   /**
      56              :    * Kept in a DLL.
      57              :    */
      58              :   struct TMH_EXCHANGES_KeysOperation *next;
      59              : 
      60              :   /**
      61              :    * Kept in a DLL.
      62              :    */
      63              :   struct TMH_EXCHANGES_KeysOperation *prev;
      64              : 
      65              :   /**
      66              :    * Function to call with the result.
      67              :    */
      68              :   TMH_EXCHANGES_Find2Continuation fc;
      69              : 
      70              :   /**
      71              :    * Closure for @e fc.
      72              :    */
      73              :   void *fc_cls;
      74              : 
      75              :   /**
      76              :    * Exchange we wait for the /keys for.
      77              :    */
      78              :   struct TMH_Exchange *my_exchange;
      79              : 
      80              :   /**
      81              :    * Task scheduled to asynchronously return the result to
      82              :    * the find continuation.
      83              :    */
      84              :   struct GNUNET_SCHEDULER_Task *at;
      85              : 
      86              : };
      87              : 
      88              : 
      89              : /**
      90              :  * Information about wire transfer fees of an exchange, by wire method.
      91              :  */
      92              : struct FeesByWireMethod
      93              : {
      94              : 
      95              :   /**
      96              :    * Kept in a DLL.
      97              :    */
      98              :   struct FeesByWireMethod *next;
      99              : 
     100              :   /**
     101              :    * Kept in a DLL.
     102              :    */
     103              :   struct FeesByWireMethod *prev;
     104              : 
     105              :   /**
     106              :    * Wire method these fees are for.
     107              :    */
     108              :   char *wire_method;
     109              : 
     110              :   /**
     111              :    * Applicable fees, NULL if unknown/error.
     112              :    */
     113              :   struct TALER_EXCHANGE_WireAggregateFees *af;
     114              : 
     115              : };
     116              : 
     117              : 
     118              : /**
     119              :  * Internal representation for an exchange
     120              :  */
     121              : struct TMH_Exchange
     122              : {
     123              : 
     124              :   /**
     125              :    * Kept in a DLL.
     126              :    */
     127              :   struct TMH_Exchange *next;
     128              : 
     129              :   /**
     130              :    * Kept in a DLL.
     131              :    */
     132              :   struct TMH_Exchange *prev;
     133              : 
     134              :   /**
     135              :    * Head of FOs pending for this exchange.
     136              :    */
     137              :   struct TMH_EXCHANGES_KeysOperation *keys_head;
     138              : 
     139              :   /**
     140              :    * Tail of FOs pending for this exchange.
     141              :    */
     142              :   struct TMH_EXCHANGES_KeysOperation *keys_tail;
     143              : 
     144              :   /**
     145              :    * (base) URL of the exchange.
     146              :    */
     147              :   char *url;
     148              : 
     149              :   /**
     150              :    * Currency offered by the exchange according to OUR configuration.
     151              :    */
     152              :   char *currency;
     153              : 
     154              :   /**
     155              :    * The keys of this exchange.
     156              :    */
     157              :   struct TALER_EXCHANGE_Keys *keys;
     158              : 
     159              :   /**
     160              :    * Head of wire fees from /wire request.
     161              :    */
     162              :   struct FeesByWireMethod *wire_fees_head;
     163              : 
     164              :   /**
     165              :    * Tail of wire fees from /wire request.
     166              :    */
     167              :   struct FeesByWireMethod *wire_fees_tail;
     168              : 
     169              :   /**
     170              :    * Task to retry downloading /keys again.
     171              :    */
     172              :   struct GNUNET_SCHEDULER_Task *retry_task;
     173              : 
     174              :   /**
     175              :    * When are we willing to force downloading again?
     176              :    */
     177              :   struct GNUNET_TIME_Absolute first_retry;
     178              : 
     179              :   /**
     180              :    * Current exponential back-off for @e retry_task.
     181              :    */
     182              :   struct GNUNET_TIME_Relative retry_delay;
     183              : 
     184              :   /**
     185              :    * Master public key of the exchange.
     186              :    */
     187              :   struct TALER_MasterPublicKeyP master_pub;
     188              : 
     189              :   /**
     190              :    * true if this exchange is from our configuration and
     191              :    * explicitly trusted, false if we need to check each
     192              :    * key to be sure it is trusted.
     193              :    */
     194              :   bool trusted;
     195              : 
     196              : };
     197              : 
     198              : 
     199              : /**
     200              :  * Head of exchanges we know about.
     201              :  */
     202              : static struct TMH_Exchange *exchange_head;
     203              : 
     204              : /**
     205              :  * Tail of exchanges we know about.
     206              :  */
     207              : static struct TMH_Exchange *exchange_tail;
     208              : 
     209              : /**
     210              :  * Our event handler listening for /keys downloads
     211              :  * being put into the database.
     212              :  */
     213              : static struct GNUNET_DB_EventHandler *keys_eh;
     214              : 
     215              : /**
     216              :  * How many exchanges do we trust (for our configured
     217              :  * currency) as per our configuration? Used for a
     218              :  * sanity-check on startup.
     219              :  */
     220              : static int trusted_exchange_count;
     221              : 
     222              : 
     223              : const struct TALER_MasterPublicKeyP *
     224           78 : TMH_EXCHANGES_get_master_pub (
     225              :   const struct TMH_Exchange *exchange)
     226              : {
     227           78 :   GNUNET_break ( (exchange->trusted) ||
     228              :                  (NULL != exchange->keys) );
     229           78 :   return &exchange->master_pub;
     230              : }
     231              : 
     232              : 
     233              : const char *
     234           78 : TMH_EXCHANGES_get_currency (
     235              :   const struct TMH_Exchange *exchange)
     236              : {
     237           78 :   return exchange->currency;
     238              : }
     239              : 
     240              : 
     241              : /**
     242              :  * Lookup exchange by @a exchange_url. Create one
     243              :  * if it does not exist.
     244              :  *
     245              :  * @param exchange_url base URL to match against
     246              :  * @return fresh entry if exchange was not yet known
     247              :  */
     248              : static struct TMH_Exchange *
     249          151 : lookup_exchange (const char *exchange_url)
     250              : {
     251          151 :   for (struct TMH_Exchange *exchange = exchange_head;
     252          151 :        NULL != exchange;
     253            0 :        exchange = exchange->next)
     254          136 :     if (0 == strcmp (exchange->url,
     255              :                      exchange_url))
     256          136 :       return exchange;
     257           15 :   return NULL;
     258              : }
     259              : 
     260              : 
     261              : bool
     262            9 : TMH_EXCHANGES_check_trusted (
     263              :   const char *exchange_url)
     264              : {
     265            9 :   struct TMH_Exchange *exchange = lookup_exchange (exchange_url);
     266              : 
     267            9 :   if (NULL == exchange)
     268            0 :     return false;
     269            9 :   return exchange->trusted;
     270              : }
     271              : 
     272              : 
     273              : /**
     274              :  * Check if we have any remaining pending requests for the
     275              :  * given @a exchange, and if we have the required data, call
     276              :  * the callback.
     277              :  *
     278              :  * @param exchange the exchange to check for pending find operations
     279              :  */
     280              : static void
     281          117 : process_find_operations (struct TMH_Exchange *exchange)
     282              : {
     283              :   struct GNUNET_TIME_Timestamp now;
     284              : 
     285          117 :   now = GNUNET_TIME_timestamp_get ();
     286          117 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     287          233 :        NULL != fbw;
     288          116 :        fbw = fbw->next)
     289              :   {
     290          116 :     while ( (NULL != fbw->af) &&
     291          116 :             (GNUNET_TIME_timestamp_cmp (fbw->af->end_date,
     292              :                                         <,
     293              :                                         now)) )
     294              :     {
     295            0 :       struct TALER_EXCHANGE_WireAggregateFees *af = fbw->af;
     296              : 
     297            0 :       fbw->af = af->next;
     298            0 :       GNUNET_free (af);
     299              :     }
     300          116 :     if (NULL == fbw->af)
     301              :     {
     302              :       /* Disagreement on the current time */
     303            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     304              :                   "Exchange has no wire fees configured for `%s' wire method\n",
     305              :                   fbw->wire_method);
     306              :     }
     307          116 :     else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
     308              :                                         >,
     309              :                                         now))
     310              :     {
     311              :       /* Disagreement on the current time */
     312            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     313              :                   "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
     314              :                   GNUNET_TIME_relative2s (
     315              :                     GNUNET_TIME_absolute_get_remaining (
     316              :                       fbw->af->start_date.abs_time),
     317              :                     true));
     318              :     }
     319              :   } /* for all wire methods */
     320              : 
     321              :   {
     322              :     struct TMH_EXCHANGES_KeysOperation *kon;
     323              : 
     324          117 :     kon = NULL;
     325          117 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     326              :                 "Processing find operations for `%s'\n",
     327              :                 exchange->url);
     328          117 :     for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head;
     329          227 :          NULL != ko;
     330          110 :          ko = kon)
     331              :     {
     332          110 :       kon = ko->next;
     333          110 :       ko->fc (ko->fc_cls,
     334              :               exchange->keys,
     335              :               exchange);
     336          110 :       TMH_EXCHANGES_keys4exchange_cancel (ko);
     337              :     }
     338              :   }
     339          117 : }
     340              : 
     341              : 
     342              : /**
     343              :  * Function called with information about the wire fees for each wire method.
     344              :  * Stores the wire fees within our internal data structures for later use.
     345              :  *
     346              :  * @param exchange connection to the exchange
     347              :  * @param master_pub public key of the exchange
     348              :  * @param num_methods number of wire methods supported
     349              :  * @param fbm wire fees by method
     350              :  * @return #GNUNET_OK on success
     351              :  */
     352              : static enum GNUNET_GenericReturnValue
     353           11 : process_wire_fees (
     354              :   struct TMH_Exchange *exchange,
     355              :   const struct TALER_MasterPublicKeyP *master_pub,
     356              :   unsigned int num_methods,
     357              :   const struct TALER_EXCHANGE_WireFeesByMethod fbm[static num_methods])
     358           11 : {
     359           22 :   for (unsigned int i = 0; i<num_methods; i++)
     360              :   {
     361           11 :     const char *wire_method = fbm[i].method;
     362           11 :     const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head;
     363              :     struct FeesByWireMethod *f;
     364              :     struct TALER_EXCHANGE_WireAggregateFees *endp;
     365              : 
     366           11 :     for (f = exchange->wire_fees_head; NULL != f; f = f->next)
     367            0 :       if (0 == strcasecmp (wire_method,
     368            0 :                            f->wire_method))
     369            0 :         break;
     370           11 :     if (NULL == f)
     371              :     {
     372           11 :       f = GNUNET_new (struct FeesByWireMethod);
     373           11 :       f->wire_method = GNUNET_strdup (wire_method);
     374           11 :       GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
     375              :                                    exchange->wire_fees_tail,
     376              :                                    f);
     377              :     }
     378           11 :     endp = f->af;
     379           11 :     while ( (NULL != endp) &&
     380            0 :             (NULL != endp->next) )
     381            0 :       endp = endp->next;
     382           11 :     while ( (NULL != endp) &&
     383           11 :             (NULL != fees) &&
     384            0 :             (GNUNET_TIME_timestamp_cmp (fees->start_date,
     385              :                                         <,
     386              :                                         endp->end_date)) )
     387            0 :       fees = fees->next;
     388           11 :     if ( (NULL != endp) &&
     389            0 :          (NULL != fees) &&
     390            0 :          (GNUNET_TIME_timestamp_cmp (fees->start_date,
     391              :                                      !=,
     392              :                                      endp->end_date)) )
     393              :     {
     394              :       /* Hole or overlap in the fee structure, not allowed! */
     395            0 :       GNUNET_break_op (0);
     396            0 :       return GNUNET_SYSERR;
     397              :     }
     398           22 :     while (NULL != fees)
     399              :     {
     400              :       struct TALER_EXCHANGE_WireAggregateFees *af;
     401              : 
     402           11 :       af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
     403           11 :       *af = *fees;
     404           11 :       af->next = NULL;
     405           11 :       if (NULL == endp)
     406           11 :         f->af = af;
     407              :       else
     408            0 :         endp->next = af;
     409           11 :       endp = af;
     410           11 :       fees = fees->next;
     411              :     } /* all fees for this method */
     412              :   } /* for all methods (i) */
     413           11 :   return GNUNET_OK;
     414              : }
     415              : 
     416              : 
     417              : /**
     418              :  * Retry getting keys from the given exchange in the closure.
     419              :  *
     420              :  * @param cls the `struct TMH_Exchange *`
     421              :  */
     422              : static void
     423           16 : retry_exchange (void *cls)
     424              : {
     425           16 :   struct TMH_Exchange *exchange = cls;
     426           16 :   struct GNUNET_DB_EventHeaderP es = {
     427           16 :     .size = ntohs (sizeof (es)),
     428           16 :     .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS)
     429              :   };
     430              : 
     431           16 :   exchange->retry_task = NULL;
     432              :   exchange->retry_delay
     433           16 :     = GNUNET_TIME_randomized_backoff (exchange->retry_delay,
     434              :                                       RETRY_BACKOFF_THRESHOLD);
     435              :   exchange->first_retry
     436           16 :     = GNUNET_TIME_relative_to_absolute (
     437              :         exchange->retry_delay);
     438              : 
     439           16 :   TMH_db->event_notify (TMH_db->cls,
     440              :                         &es,
     441           16 :                         exchange->url,
     442           16 :                         strlen (exchange->url) + 1);
     443           16 : }
     444              : 
     445              : 
     446              : /**
     447              :  * Task to asynchronously return keys operation result to caller.
     448              :  *
     449              :  * @param cls a `struct TMH_EXCHANGES_KeysOperation`
     450              :  */
     451              : static void
     452          106 : return_keys (void *cls)
     453              : {
     454          106 :   struct TMH_EXCHANGES_KeysOperation *fo = cls;
     455          106 :   struct TMH_Exchange *exchange = fo->my_exchange;
     456              : 
     457          106 :   fo->at = NULL;
     458          106 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     459              :               "Returning key data for %s instantly\n",
     460              :               exchange->url);
     461          106 :   process_find_operations (exchange);
     462          106 : }
     463              : 
     464              : 
     465              : struct TMH_EXCHANGES_KeysOperation *
     466          110 : TMH_EXCHANGES_keys4exchange (
     467              :   const char *chosen_exchange,
     468              :   bool force_download,
     469              :   TMH_EXCHANGES_Find2Continuation fc,
     470              :   void *fc_cls)
     471              : {
     472              :   struct TMH_Exchange *exchange;
     473              :   struct TMH_EXCHANGES_KeysOperation *fo;
     474              : 
     475          110 :   if (NULL == TMH_curl_ctx)
     476              :   {
     477            0 :     GNUNET_break (0);
     478            0 :     return NULL;
     479              :   }
     480          110 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     481              :               "Trying to find chosen exchange `%s'\n",
     482              :               chosen_exchange);
     483          110 :   exchange = lookup_exchange (chosen_exchange);
     484          110 :   if (NULL == exchange)
     485              :   {
     486            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     487              :                 "Exchange `%s' not configured\n",
     488              :                 chosen_exchange);
     489            0 :     return NULL;
     490              :   }
     491          110 :   if (! exchange->trusted)
     492              :   {
     493            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     494              :                 "Exchange `%s' not trusted\n",
     495              :                 chosen_exchange);
     496            0 :     return NULL;
     497              :   }
     498          110 :   fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation);
     499          110 :   fo->fc = fc;
     500          110 :   fo->fc_cls = fc_cls;
     501          110 :   fo->my_exchange = exchange;
     502          110 :   GNUNET_CONTAINER_DLL_insert (exchange->keys_head,
     503              :                                exchange->keys_tail,
     504              :                                fo);
     505          110 :   if ( (NULL == exchange->keys) &&
     506            2 :        (! force_download) )
     507              :   {
     508            1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     509              :                 "Waiting for `%skeys' already, failing query instantly\n",
     510              :                 exchange->url);
     511            1 :     GNUNET_assert (NULL == fo->at);
     512            1 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     513              :                                        fo);
     514            1 :     return fo;
     515              :   }
     516          109 :   if ( (NULL != exchange->keys) &&
     517          216 :        (! force_download) &&
     518          108 :        (GNUNET_TIME_absolute_is_future (
     519          108 :           exchange->keys->key_data_expiration.abs_time)) )
     520              :   {
     521              :     /* We have a valid reply, immediately return result */
     522          108 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     523              :                 "The exchange `%s' is ready\n",
     524              :                 exchange->url);
     525          108 :     GNUNET_assert (NULL == fo->at);
     526          108 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     527              :                                        fo);
     528          108 :     return fo;
     529              :   }
     530            2 :   if ( (force_download) &&
     531            1 :        (GNUNET_TIME_absolute_is_future (exchange->first_retry)) &&
     532            0 :        (NULL != exchange->keys) )
     533              :   {
     534              :     /* Return results immediately. */
     535            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     536              :                 "Earliest retry is in the future, returning keys now\n");
     537            0 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     538              :                                        fo);
     539              :     /* *no* return here, we MAY schedule a 'retry_task' in the
     540              :        next block if there isn't one yet */
     541              :   }
     542            1 :   if (NULL == exchange->retry_task)
     543              :     exchange->retry_task
     544            1 :       = GNUNET_SCHEDULER_add_at (exchange->first_retry,
     545              :                                  &retry_exchange,
     546              :                                  exchange);
     547            1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     548              :               "Next %skeys request scheduled for %s\n",
     549              :               exchange->url,
     550              :               GNUNET_TIME_absolute2s (
     551              :                 exchange->first_retry));
     552              :   /* No activity to launch, we are already doing so. */
     553            1 :   return fo;
     554              : }
     555              : 
     556              : 
     557              : void
     558          110 : TMH_EXCHANGES_keys4exchange_cancel (
     559              :   struct TMH_EXCHANGES_KeysOperation *fo)
     560              : {
     561          110 :   struct TMH_Exchange *exchange = fo->my_exchange;
     562              : 
     563          110 :   if (NULL != fo->at)
     564              :   {
     565            3 :     GNUNET_SCHEDULER_cancel (fo->at);
     566            3 :     fo->at = NULL;
     567              :   }
     568          110 :   GNUNET_CONTAINER_DLL_remove (exchange->keys_head,
     569              :                                exchange->keys_tail,
     570              :                                fo);
     571          110 :   GNUNET_free (fo);
     572          110 : }
     573              : 
     574              : 
     575              : /**
     576              :  * Obtain applicable fees for @a exchange and @a wire_method.
     577              :  *
     578              :  * @param exchange the exchange to query
     579              :  * @param now current time
     580              :  * @param wire_method the wire method we want the fees for
     581              :  * @return NULL if we do not have fees for this method yet
     582              :  */
     583              : static const struct FeesByWireMethod *
     584           35 : get_wire_fees (const struct TMH_Exchange *exchange,
     585              :                struct GNUNET_TIME_Timestamp now,
     586              :                const char *wire_method)
     587              : {
     588           35 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     589           35 :        NULL != fbw;
     590            0 :        fbw = fbw->next)
     591              :   {
     592           35 :     if (0 == strcasecmp (fbw->wire_method,
     593              :                          wire_method) )
     594              :     {
     595              :       struct TALER_EXCHANGE_WireAggregateFees *af;
     596              : 
     597              :       /* Advance through list up to current time */
     598           35 :       while ( (NULL != (af = fbw->af)) &&
     599           35 :               (GNUNET_TIME_timestamp_cmp (now,
     600              :                                           >=,
     601              :                                           af->end_date)) )
     602              :       {
     603            0 :         fbw->af = af->next;
     604            0 :         GNUNET_free (af);
     605              :       }
     606           35 :       return fbw;
     607              :     }
     608            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     609              :                 "Exchange supports `%s' as a wire method (but we do not use that one)\n",
     610              :                 fbw->wire_method);
     611              :   }
     612            0 :   return NULL;
     613              : }
     614              : 
     615              : 
     616              : /**
     617              :  * Free @a exchange.
     618              :  *
     619              :  * @param[in] exchange entry to free
     620              :  */
     621              : static void
     622           15 : free_exchange_entry (struct TMH_Exchange *exchange)
     623              : {
     624              :   struct FeesByWireMethod *f;
     625              : 
     626           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     627              :               "Releasing %s exchange %s\n",
     628              :               exchange->trusted ? "trusted" : "untrusted",
     629              :               exchange->url);
     630           15 :   GNUNET_CONTAINER_DLL_remove (exchange_head,
     631              :                                exchange_tail,
     632              :                                exchange);
     633           26 :   while (NULL != (f = exchange->wire_fees_head))
     634              :   {
     635              :     struct TALER_EXCHANGE_WireAggregateFees *af;
     636              : 
     637           11 :     GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
     638              :                                  exchange->wire_fees_tail,
     639              :                                  f);
     640           22 :     while (NULL != (af = f->af))
     641              :     {
     642           11 :       f->af = af->next;
     643           11 :       GNUNET_free (af);
     644              :     }
     645           11 :     GNUNET_free (f->wire_method);
     646           11 :     GNUNET_free (f);
     647              :   }
     648           15 :   TALER_EXCHANGE_keys_decref (exchange->keys);
     649           15 :   exchange->keys = NULL;
     650           15 :   if (NULL != exchange->retry_task)
     651              :   {
     652            0 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     653            0 :     exchange->retry_task = NULL;
     654              :   }
     655           15 :   GNUNET_assert (NULL == exchange->keys_head);
     656           15 :   GNUNET_assert (NULL == exchange->keys_tail);
     657           15 :   GNUNET_free (exchange->currency);
     658           15 :   GNUNET_free (exchange->url);
     659           15 :   GNUNET_free (exchange);
     660           15 : }
     661              : 
     662              : 
     663              : enum GNUNET_GenericReturnValue
     664           35 : TMH_EXCHANGES_lookup_wire_fee (
     665              :   const struct TMH_Exchange *exchange,
     666              :   const char *wire_method,
     667              :   struct TALER_Amount *wire_fee)
     668              : {
     669              :   const struct FeesByWireMethod *fbm;
     670              :   const struct TALER_EXCHANGE_WireAggregateFees *af;
     671              : 
     672           35 :   fbm = get_wire_fees (exchange,
     673              :                        GNUNET_TIME_timestamp_get (),
     674              :                        wire_method);
     675           35 :   if (NULL == fbm)
     676            0 :     return GNUNET_NO;
     677           35 :   af = fbm->af;
     678           35 :   *wire_fee = af->fees.wire;
     679           35 :   return GNUNET_OK;
     680              : }
     681              : 
     682              : 
     683              : enum TMH_ExchangeStatus
     684           99 : TMH_exchange_check_debit (
     685              :   const char *instance_id,
     686              :   const struct TMH_Exchange *exchange,
     687              :   const struct TMH_WireMethod *wm,
     688              :   struct TALER_Amount *max_amount)
     689              : {
     690           99 :   const struct TALER_EXCHANGE_Keys *keys = exchange->keys;
     691           99 :   bool have_kyc = false;
     692           99 :   bool no_access_token = true;
     693           99 :   enum TMH_ExchangeStatus retry_ok = 0;
     694              : 
     695           99 :   if (GNUNET_TIME_absolute_is_past (exchange->first_retry))
     696            0 :     retry_ok = TMH_ES_RETRY_OK;
     697              : 
     698           99 :   if (NULL == keys)
     699            0 :     return TMH_ES_NO_KEYS | retry_ok;
     700           99 :   if (0 != strcasecmp (keys->currency,
     701           99 :                        max_amount->currency))
     702              :   {
     703            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     704              :                 "Currency mismatch: exchange %s uses %s, we need %s\n",
     705              :                 exchange->url,
     706              :                 keys->currency,
     707              :                 max_amount->currency);
     708            0 :     return TMH_ES_NO_CURR | retry_ok;
     709              :   }
     710              :   {
     711              :     struct TALER_NormalizedPayto np;
     712              :     bool account_ok;
     713              : 
     714           99 :     np = TALER_payto_normalize (wm->payto_uri);
     715           99 :     account_ok = TALER_EXCHANGE_keys_test_account_allowed (keys,
     716              :                                                            false,
     717              :                                                            np);
     718           99 :     GNUNET_free (np.normalized_payto);
     719           99 :     if (! account_ok)
     720            0 :       return TMH_ES_NO_ACC | retry_ok;
     721              :   }
     722           99 :   if (! keys->kyc_enabled)
     723           95 :     return TMH_ES_OK | retry_ok;
     724              : 
     725              :   {
     726            4 :     json_t *jlimits = NULL;
     727              :     enum GNUNET_DB_QueryStatus qs;
     728              : 
     729            4 :     qs = TMH_db->get_kyc_limits (TMH_db->cls,
     730              :                                  wm->payto_uri,
     731              :                                  instance_id,
     732            4 :                                  exchange->url,
     733              :                                  &have_kyc,
     734              :                                  &no_access_token,
     735              :                                  &jlimits);
     736            4 :     GNUNET_break (qs >= 0);
     737            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     738              :                 "get_kyc_limits for %s at %s returned %s/%s\n",
     739              :                 wm->payto_uri.full_payto,
     740              :                 exchange->url,
     741              :                 have_kyc ? "KYC OK" : "KYC missing",
     742              :                 NULL == jlimits ? "default limits" : "custom limits");
     743            4 :     if ( (qs > 0) &&
     744            4 :          (NULL != jlimits) )
     745              :     {
     746              :       json_t *jlimit;
     747              :       size_t idx;
     748              :       struct TALER_Amount kyc_limit;
     749            4 :       bool unlimited = true;
     750              : 
     751            8 :       json_array_foreach (jlimits, idx, jlimit)
     752              :       {
     753              :         enum TALER_KYCLOGIC_KycTriggerEvent ot;
     754              :         struct TALER_Amount threshold;
     755            4 :         bool soft_limit = false;
     756              :         struct GNUNET_JSON_Specification spec[] = {
     757            4 :           TALER_JSON_spec_kycte ("operation_type",
     758              :                                  &ot),
     759            4 :           TALER_JSON_spec_amount_any ("threshold",
     760              :                                       &threshold),
     761            4 :           GNUNET_JSON_spec_mark_optional (
     762              :             GNUNET_JSON_spec_bool ("soft_limit",
     763              :                                    &soft_limit),
     764              :             NULL),
     765            4 :           GNUNET_JSON_spec_end ()
     766              :         };
     767              : 
     768            4 :         if (GNUNET_OK !=
     769            4 :             GNUNET_JSON_parse (jlimit,
     770              :                                spec,
     771              :                                NULL, NULL))
     772              :         {
     773            0 :           GNUNET_break (0);
     774            4 :           continue;
     775              :         }
     776            4 :         if (soft_limit)
     777            4 :           continue;
     778            0 :         if ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT != ot) &&
     779            0 :              (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION != ot) )
     780            0 :           continue;
     781            0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     782              :                     "KYC rule %u with limit %s applies\n",
     783              :                     (unsigned int) idx,
     784              :                     TALER_amount2s (&threshold));
     785            0 :         if (unlimited)
     786              :         {
     787            0 :           unlimited = false;
     788            0 :           kyc_limit = threshold;
     789              :         }
     790              :         else
     791              :         {
     792            0 :           TALER_amount_min (&kyc_limit,
     793              :                             &kyc_limit,
     794              :                             &threshold);
     795              :         }
     796              :       }
     797            4 :       json_decref (jlimits);
     798              :       /* We had custom rules, do not evaluate default rules */
     799            4 :       if (! unlimited)
     800            0 :         TALER_amount_min (max_amount,
     801              :                           max_amount,
     802              :                           &kyc_limit);
     803            4 :       return TMH_ES_OK | retry_ok;
     804              :     } /* END of if qs > 0, NULL != jlimits */
     805              :   }
     806              : 
     807              :   /* Check zero limits *only* if we did no KYC process at all yet.
     808              :      Because if we did, there is at least a chance that those have
     809              :      been lifted. */
     810            0 :   if ( (no_access_token) ||
     811            0 :        ( (! have_kyc) &&
     812            0 :          (TALER_EXCHANGE_keys_evaluate_zero_limits (
     813              :             keys,
     814            0 :             TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     815            0 :           TALER_EXCHANGE_keys_evaluate_zero_limits (
     816              :             keys,
     817              :             TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) )
     818              :   {
     819            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     820              :                 "KYC requirements of %s not satisfied\n",
     821              :                 exchange->url);
     822            0 :     GNUNET_assert (GNUNET_OK ==
     823              :                    TALER_amount_set_zero (
     824              :                      max_amount->currency,
     825              :                      max_amount));
     826            0 :     return TMH_ES_OK | retry_ok;
     827              :   }
     828              :   /* In any case, abide by hard limits (unless we have custom rules). */
     829            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     830              :               "Evaluating default hard limits of %s\n",
     831              :               exchange->url);
     832            0 :   TALER_EXCHANGE_keys_evaluate_hard_limits (
     833              :     keys,
     834              :     TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
     835              :     max_amount);
     836            0 :   TALER_EXCHANGE_keys_evaluate_hard_limits (
     837              :     keys,
     838              :     TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
     839              :     max_amount);
     840            0 :   if (TALER_EXCHANGE_keys_evaluate_zero_limits (
     841              :         keys,
     842            0 :         TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     843            0 :       TALER_EXCHANGE_keys_evaluate_zero_limits (
     844              :         keys,
     845              :         TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION))
     846              :   {
     847            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     848              :                 "Operation is zero-limited by default\n");
     849            0 :     GNUNET_assert (GNUNET_OK ==
     850              :                    TALER_amount_set_zero (max_amount->currency,
     851              :                                           max_amount));
     852              :   }
     853            0 :   return TMH_ES_OK | retry_ok;
     854              : }
     855              : 
     856              : 
     857              : void
     858           78 : TMH_exchange_get_trusted (TMH_ExchangeCallback cb,
     859              :                           void *cb_cls)
     860              : {
     861           78 :   for (const struct TMH_Exchange *exchange = exchange_head;
     862          156 :        NULL != exchange;
     863           78 :        exchange = exchange->next)
     864              :   {
     865           78 :     if (! exchange->trusted)
     866              :     {
     867            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     868              :                   "Exchange %s not trusted, skipping!\n",
     869              :                   exchange->url);
     870            0 :       continue;
     871              :     }
     872           78 :     cb (cb_cls,
     873           78 :         exchange->url,
     874              :         exchange);
     875              :   }
     876           78 : }
     877              : 
     878              : 
     879              : bool
     880          144 : TMH_test_exchange_configured_for_currency (
     881              :   const char *currency)
     882              : {
     883          144 :   for (const struct TMH_Exchange *exchange = exchange_head;
     884          202 :        NULL != exchange;
     885           58 :        exchange = exchange->next)
     886              :   {
     887          144 :     if (! exchange->trusted)
     888            0 :       continue;
     889          144 :     if (NULL == exchange->currency)
     890            0 :       continue;
     891          144 :     if (0 == strcmp (currency,
     892          144 :                      exchange->currency))
     893           86 :       return true;
     894              :   }
     895           58 :   return false;
     896              : }
     897              : 
     898              : 
     899              : /**
     900              :  * (Re)load of keys from DB.
     901              :  *
     902              :  * @param exchange exchange to reload keys of
     903              :  */
     904              : static void
     905           26 : reload_exchange_keys (struct TMH_Exchange *exchange)
     906              : {
     907              :   enum GNUNET_DB_QueryStatus qs;
     908              :   struct TALER_EXCHANGE_Keys *keys;
     909              :   struct GNUNET_TIME_Absolute first_retry;
     910              : 
     911           26 :   qs = TMH_db->select_exchange_keys (TMH_db->cls,
     912           26 :                                      exchange->url,
     913              :                                      &first_retry,
     914              :                                      &keys);
     915           26 :   if (qs < 0)
     916              :   {
     917            0 :     GNUNET_break (0);
     918           15 :     return;
     919              :   }
     920           26 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     921              :   {
     922           15 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     923              :                 "No keys yet for `%s'\n",
     924              :                 exchange->url);
     925           15 :     return;
     926              :   }
     927           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     928              :               "Loading latest keys of `%s' from database\n",
     929              :               exchange->url);
     930           11 :   exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
     931           11 :   exchange->first_retry = first_retry;
     932           11 :   if (NULL == exchange->currency)
     933            0 :     exchange->currency = GNUNET_strdup (keys->currency);
     934           11 :   if (0 != strcmp (keys->currency,
     935           11 :                    exchange->currency))
     936              :   {
     937            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     938              :                 "/keys cached in our database are for currency `%s', but we expected `%s'\n",
     939              :                 keys->currency,
     940              :                 exchange->currency);
     941            0 :     return;
     942              :   }
     943           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     944              :               "Loaded /keys from database with %u accounts\n",
     945              :               keys->accounts_len);
     946           11 :   if (GNUNET_OK !=
     947           11 :       process_wire_fees (exchange,
     948           11 :                          &keys->master_pub,
     949           11 :                          keys->fees_len,
     950           11 :                          keys->fees))
     951              :   {
     952              :     /* invalid wire fee specification given */
     953            0 :     GNUNET_break_op (0);
     954              :     /* but: we can continue anyway, things may just not
     955              :        work, but probably better than to not keep going. */
     956            0 :     return;
     957              :   }
     958              : 
     959           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     960              :               "Reloaded /keys of %s from database\n",
     961              :               exchange->url);
     962           11 :   TALER_EXCHANGE_keys_decref (exchange->keys);
     963           11 :   exchange->keys = keys;
     964           11 :   if ( (exchange->trusted) &&
     965           11 :        (0 != GNUNET_memcmp (&exchange->master_pub,
     966              :                             &keys->master_pub)) )
     967              :   {
     968              :     /* master pub differs => do not trust the exchange (without auditor) */
     969            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     970              :                 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
     971              :                 exchange->url);
     972            0 :     exchange->trusted = false;
     973              :   }
     974           11 :   if (! exchange->trusted)
     975              :   {
     976            0 :     exchange->master_pub = keys->master_pub;
     977            0 :     for (struct TMH_Exchange *e = exchange_head;
     978            0 :          NULL != e;
     979            0 :          e = e->next)
     980              :     {
     981            0 :       if (e == exchange)
     982            0 :         continue;
     983            0 :       if (! e->trusted)
     984            0 :         continue;
     985            0 :       if (0 ==
     986            0 :           GNUNET_memcmp (&e->master_pub,
     987              :                          &exchange->master_pub))
     988            0 :         exchange->trusted = true; /* same exchange, different URL => trust applies */
     989              :     }
     990              :   }
     991              : 
     992           11 :   process_find_operations (exchange);
     993              : }
     994              : 
     995              : 
     996              : /**
     997              :  * Function called on each configuration section. Finds sections
     998              :  * about exchanges, parses the entries.
     999              :  *
    1000              :  * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
    1001              :  * @param section name of the section
    1002              :  */
    1003              : static void
    1004          603 : accept_exchanges (void *cls,
    1005              :                   const char *section)
    1006              : {
    1007          603 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1008              :   char *url;
    1009              :   char *mks;
    1010              :   struct TMH_Exchange *exchange;
    1011              :   char *currency;
    1012              : 
    1013          603 :   if (GNUNET_SYSERR == trusted_exchange_count)
    1014          588 :     return;
    1015          603 :   if (0 != strncasecmp (section,
    1016              :                         "merchant-exchange-",
    1017              :                         strlen ("merchant-exchange-")))
    1018          558 :     return;
    1019           45 :   if (GNUNET_YES ==
    1020           45 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    1021              :                                             section,
    1022              :                                             "DISABLED"))
    1023           30 :     return;
    1024           15 :   if (GNUNET_OK !=
    1025           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1026              :                                              section,
    1027              :                                              "EXCHANGE_BASE_URL",
    1028              :                                              &url))
    1029              :   {
    1030            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1031              :                                section,
    1032              :                                "EXCHANGE_BASE_URL");
    1033            0 :     return;
    1034              :   }
    1035           15 :   exchange = lookup_exchange (url);
    1036           15 :   if (NULL != exchange)
    1037              :   {
    1038            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1039              :                                section,
    1040              :                                "EXCHANGE_BASE_URL",
    1041              :                                "same base URL specified again");
    1042            0 :     GNUNET_free (url);
    1043            0 :     return;
    1044              :   }
    1045           15 :   if (GNUNET_OK !=
    1046           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1047              :                                              section,
    1048              :                                              "CURRENCY",
    1049              :                                              &currency))
    1050              :   {
    1051            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1052              :                                section,
    1053              :                                "CURRENCY");
    1054            0 :     GNUNET_free (url);
    1055            0 :     return;
    1056              :   }
    1057           15 :   exchange = GNUNET_new (struct TMH_Exchange);
    1058           15 :   exchange->url = url;
    1059           15 :   exchange->currency = currency;
    1060           15 :   GNUNET_CONTAINER_DLL_insert (exchange_head,
    1061              :                                exchange_tail,
    1062              :                                exchange);
    1063           15 :   if (GNUNET_OK ==
    1064           15 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1065              :                                              section,
    1066              :                                              "MASTER_KEY",
    1067              :                                              &mks))
    1068              :   {
    1069           15 :     if (GNUNET_OK ==
    1070           15 :         GNUNET_CRYPTO_eddsa_public_key_from_string (
    1071              :           mks,
    1072              :           strlen (mks),
    1073              :           &exchange->master_pub.eddsa_pub))
    1074              :     {
    1075           15 :       exchange->trusted = true;
    1076           15 :       trusted_exchange_count++;
    1077              :     }
    1078              :     else
    1079              :     {
    1080            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1081              :                                  section,
    1082              :                                  "MASTER_KEY",
    1083              :                                  "malformed EdDSA key");
    1084              :     }
    1085           15 :     GNUNET_free (mks);
    1086              :   }
    1087              :   else
    1088              :   {
    1089            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1090              :                 "MASTER_KEY missing in section '%s', not trusting exchange\n",
    1091              :                 section);
    1092              :   }
    1093           15 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1094              :               "Setup exchange %s as %s\n",
    1095              :               exchange->url,
    1096              :               exchange->trusted ? "trusted" : "untrusted");
    1097           15 :   reload_exchange_keys (exchange);
    1098           15 :   if (NULL != exchange->retry_task)
    1099              :   {
    1100            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1101              :                 "Exchange at `%s' configured in multiple configuration sections (see `%s')!\n",
    1102              :                 exchange->url,
    1103              :                 section);
    1104            0 :     trusted_exchange_count = GNUNET_SYSERR;
    1105            0 :     return;
    1106              :   }
    1107              :   exchange->retry_task
    1108           15 :     = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1109              :                                 exchange);
    1110              : }
    1111              : 
    1112              : 
    1113              : /**
    1114              :  * Trigger (re)loading of keys from DB.
    1115              :  *
    1116              :  * @param cls NULL
    1117              :  * @param extra base URL of the exchange that changed
    1118              :  * @param extra_len number of bytes in @a extra
    1119              :  */
    1120              : static void
    1121           11 : update_exchange_keys (void *cls,
    1122              :                       const void *extra,
    1123              :                       size_t extra_len)
    1124              : {
    1125           11 :   const char *url = extra;
    1126              :   struct TMH_Exchange *exchange;
    1127              : 
    1128           11 :   if ( (NULL == extra) ||
    1129              :        (0 == extra_len) )
    1130              :   {
    1131            0 :     GNUNET_break (0);
    1132            0 :     return;
    1133              :   }
    1134           11 :   if ('\0' != url[extra_len - 1])
    1135              :   {
    1136            0 :     GNUNET_break (0);
    1137            0 :     return;
    1138              :   }
    1139           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1140              :               "Received keys change notification: reload `%s'\n",
    1141              :               url);
    1142           11 :   exchange = lookup_exchange (url);
    1143           11 :   GNUNET_break (NULL != exchange);
    1144           11 :   if (NULL != exchange)
    1145           11 :     reload_exchange_keys (exchange);
    1146              : }
    1147              : 
    1148              : 
    1149              : bool
    1150           35 : TMH_EXCHANGES_is_below_limit (
    1151              :   const struct TALER_EXCHANGE_Keys *keys,
    1152              :   enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
    1153              :   const struct TALER_Amount *amount)
    1154              : {
    1155           35 :   if (NULL == keys)
    1156              :   {
    1157              :     /* should only be called after we have keys! */
    1158            0 :     GNUNET_break (0);
    1159            0 :     return false;
    1160              :   }
    1161           35 :   for (unsigned int i = 0; i<keys->hard_limits_length; i++)
    1162              :   {
    1163            0 :     const struct TALER_EXCHANGE_AccountLimit *al
    1164            0 :       = &keys->hard_limits[i];
    1165              : 
    1166            0 :     if (operation_type != al->operation_type)
    1167            0 :       continue;
    1168            0 :     if (-1 ==
    1169            0 :         TALER_amount_cmp (&al->threshold,
    1170              :                           amount))
    1171              :       /* -1: threshold < amount */
    1172            0 :       return false;
    1173              :   }
    1174           35 :   return true;
    1175              : }
    1176              : 
    1177              : 
    1178              : void
    1179            6 : TMH_EXCHANGES_get_limit (
    1180              :   const char *exchange_url,
    1181              :   enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
    1182              :   struct TALER_Amount *amount)
    1183              : {
    1184              :   struct TMH_Exchange *exchange;
    1185              :   const struct TALER_EXCHANGE_Keys *keys;
    1186              : 
    1187            6 :   exchange = lookup_exchange (exchange_url);
    1188            6 :   if ( (NULL == exchange) ||
    1189            6 :        (NULL == (keys = exchange->keys)) )
    1190              :   {
    1191            0 :     GNUNET_assert (GNUNET_OK ==
    1192              :                    TALER_amount_set_zero (
    1193              :                      amount->currency,
    1194              :                      amount));
    1195            0 :     return;
    1196              :   }
    1197            6 :   for (unsigned int i = 0; i<keys->hard_limits_length; i++)
    1198              :   {
    1199            0 :     const struct TALER_EXCHANGE_AccountLimit *al
    1200            0 :       = &keys->hard_limits[i];
    1201              : 
    1202            0 :     if (operation_type != al->operation_type)
    1203            0 :       continue;
    1204            0 :     TALER_amount_min (amount,
    1205              :                       amount,
    1206              :                       &al->threshold);
    1207              :   }
    1208              : }
    1209              : 
    1210              : 
    1211              : enum GNUNET_GenericReturnValue
    1212           15 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    1213              : {
    1214              :   /* get exchanges from the merchant configuration and try to connect to them */
    1215              :   {
    1216           15 :     struct GNUNET_DB_EventHeaderP es = {
    1217           15 :       .size = ntohs (sizeof (es)),
    1218           15 :       .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    1219              :     };
    1220              : 
    1221           15 :     GNUNET_assert (NULL == keys_eh);
    1222           30 :     keys_eh = TMH_db->event_listen (TMH_db->cls,
    1223              :                                     &es,
    1224           15 :                                     GNUNET_TIME_UNIT_FOREVER_REL,
    1225              :                                     &update_exchange_keys,
    1226              :                                     NULL);
    1227              :   }
    1228           15 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1229              :                                          &accept_exchanges,
    1230              :                                          (void *) cfg);
    1231              :   /* build JSON with list of trusted exchanges (will be included in contracts) */
    1232           15 :   return trusted_exchange_count;
    1233              : }
    1234              : 
    1235              : 
    1236              : void
    1237           15 : TMH_EXCHANGES_done ()
    1238              : {
    1239           15 :   if (NULL != keys_eh)
    1240              :   {
    1241           15 :     TMH_db->event_listen_cancel (keys_eh);
    1242           15 :     keys_eh = NULL;
    1243              :   }
    1244           30 :   while (NULL != exchange_head)
    1245           15 :     free_exchange_entry (exchange_head);
    1246           15 : }
    1247              : 
    1248              : 
    1249              : /* end of taler-merchant-httpd_exchanges.c */
        

Generated by: LCOV version 2.0-1