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

          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         152 : lookup_exchange (const char *exchange_url)
     250             : {
     251         152 :   for (struct TMH_Exchange *exchange = exchange_head;
     252         152 :        NULL != exchange;
     253           0 :        exchange = exchange->next)
     254         137 :     if (0 == strcmp (exchange->url,
     255             :                      exchange_url))
     256         137 :       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         232 :        NULL != fbw;
     288         115 :        fbw = fbw->next)
     289             :   {
     290         115 :     while ( (NULL != fbw->af) &&
     291         115 :             (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         115 :     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         115 :     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         228 :          NULL != ko;
     330         111 :          ko = kon)
     331             :     {
     332         111 :       kon = ko->next;
     333         111 :       ko->fc (ko->fc_cls,
     334             :               exchange->keys,
     335             :               exchange);
     336         111 :       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          17 : retry_exchange (void *cls)
     424             : {
     425          17 :   struct TMH_Exchange *exchange = cls;
     426          17 :   struct GNUNET_DB_EventHeaderP es = {
     427          17 :     .size = ntohs (sizeof (es)),
     428          17 :     .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS)
     429             :   };
     430             : 
     431          17 :   exchange->retry_task = NULL;
     432             :   exchange->retry_delay
     433          17 :     = GNUNET_TIME_randomized_backoff (exchange->retry_delay,
     434             :                                       RETRY_BACKOFF_THRESHOLD);
     435             :   exchange->first_retry
     436          17 :     = GNUNET_TIME_relative_to_absolute (
     437             :         exchange->retry_delay);
     438             : 
     439          17 :   TMH_db->event_notify (TMH_db->cls,
     440             :                         &es,
     441          17 :                         exchange->url,
     442          17 :                         strlen (exchange->url) + 1);
     443          17 : }
     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         111 : 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         111 :   if (NULL == TMH_curl_ctx)
     476             :   {
     477           0 :     GNUNET_break (0);
     478           0 :     return NULL;
     479             :   }
     480         111 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     481             :               "Trying to find chosen exchange `%s'\n",
     482             :               chosen_exchange);
     483         111 :   exchange = lookup_exchange (chosen_exchange);
     484         111 :   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         111 :   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         111 :   fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation);
     499         111 :   fo->fc = fc;
     500         111 :   fo->fc_cls = fc_cls;
     501         111 :   fo->my_exchange = exchange;
     502         111 :   GNUNET_CONTAINER_DLL_insert (exchange->keys_head,
     503             :                                exchange->keys_tail,
     504             :                                fo);
     505         111 :   if ( (NULL == exchange->keys) &&
     506           4 :        (! force_download) )
     507             :   {
     508           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     509             :                 "Waiting for `%skeys' already, failing query instantly\n",
     510             :                 exchange->url);
     511           2 :     GNUNET_assert (NULL == fo->at);
     512           2 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     513             :                                        fo);
     514           2 :     return fo;
     515             :   }
     516         109 :   if ( (NULL != exchange->keys) &&
     517         214 :        (! force_download) &&
     518         107 :        (GNUNET_TIME_absolute_is_future (
     519         107 :           exchange->keys->key_data_expiration.abs_time)) )
     520             :   {
     521             :     /* We have a valid reply, immediately return result */
     522         107 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     523             :                 "The exchange `%s' is ready\n",
     524             :                 exchange->url);
     525         107 :     GNUNET_assert (NULL == fo->at);
     526         107 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     527             :                                        fo);
     528         107 :     return fo;
     529             :   }
     530           4 :   if ( (force_download) &&
     531           2 :        (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           2 :   if (NULL == exchange->retry_task)
     543             :     exchange->retry_task
     544           2 :       = GNUNET_SCHEDULER_add_at (exchange->first_retry,
     545             :                                  &retry_exchange,
     546             :                                  exchange);
     547           2 :   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           2 :   return fo;
     554             : }
     555             : 
     556             : 
     557             : void
     558         111 : TMH_EXCHANGES_keys4exchange_cancel (
     559             :   struct TMH_EXCHANGES_KeysOperation *fo)
     560             : {
     561         111 :   struct TMH_Exchange *exchange = fo->my_exchange;
     562             : 
     563         111 :   if (NULL != fo->at)
     564             :   {
     565           3 :     GNUNET_SCHEDULER_cancel (fo->at);
     566           3 :     fo->at = NULL;
     567             :   }
     568         111 :   GNUNET_CONTAINER_DLL_remove (exchange->keys_head,
     569             :                                exchange->keys_tail,
     570             :                                fo);
     571         111 :   GNUNET_free (fo);
     572         111 : }
     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          79 : TMH_exchange_get_trusted (TMH_ExchangeCallback cb,
     859             :                           void *cb_cls)
     860             : {
     861          79 :   for (const struct TMH_Exchange *exchange = exchange_head;
     862         158 :        NULL != exchange;
     863          79 :        exchange = exchange->next)
     864             :   {
     865          79 :     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          79 :     cb (cb_cls,
     873          79 :         exchange->url,
     874             :         exchange);
     875             :   }
     876          79 : }
     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 1.16