LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_exchanges.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 294 409 71.9 %
Date: 2025-06-23 16:22:09 Functions: 22 22 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          88 : TMH_EXCHANGES_get_master_pub (
     225             :   const struct TMH_Exchange *exchange)
     226             : {
     227          88 :   GNUNET_break ( (exchange->trusted) ||
     228             :                  (NULL != exchange->keys) );
     229          88 :   return &exchange->master_pub;
     230             : }
     231             : 
     232             : 
     233             : const char *
     234          26 : TMH_EXCHANGES_get_currency (
     235             :   const struct TMH_Exchange *exchange)
     236             : {
     237          26 :   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         221 : lookup_exchange (const char *exchange_url)
     250             : {
     251         221 :   for (struct TMH_Exchange *exchange = exchange_head;
     252         359 :        NULL != exchange;
     253         138 :        exchange = exchange->next)
     254         331 :     if (0 == strcmp (exchange->url,
     255             :                      exchange_url))
     256         193 :       return exchange;
     257          28 :   return NULL;
     258             : }
     259             : 
     260             : 
     261             : /**
     262             :  * Check if we have any remaining pending requests for the
     263             :  * given @a exchange, and if we have the required data, call
     264             :  * the callback.
     265             :  *
     266             :  * @param exchange the exchange to check for pending find operations
     267             :  */
     268             : static void
     269         191 : process_find_operations (struct TMH_Exchange *exchange)
     270             : {
     271             :   struct GNUNET_TIME_Timestamp now;
     272             : 
     273         191 :   now = GNUNET_TIME_timestamp_get ();
     274         191 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     275         380 :        NULL != fbw;
     276         189 :        fbw = fbw->next)
     277             :   {
     278         189 :     while ( (NULL != fbw->af) &&
     279         189 :             (GNUNET_TIME_timestamp_cmp (fbw->af->end_date,
     280             :                                         <,
     281             :                                         now)) )
     282             :     {
     283           0 :       struct TALER_EXCHANGE_WireAggregateFees *af = fbw->af;
     284             : 
     285           0 :       fbw->af = af->next;
     286           0 :       GNUNET_free (af);
     287             :     }
     288         189 :     if (NULL == fbw->af)
     289             :     {
     290             :       /* Disagreement on the current time */
     291           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     292             :                   "Exchange has no wire fees configured for `%s' wire method\n",
     293             :                   fbw->wire_method);
     294             :     }
     295         189 :     else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
     296             :                                         >,
     297             :                                         now))
     298             :     {
     299             :       /* Disagreement on the current time */
     300           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     301             :                   "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
     302             :                   GNUNET_TIME_relative2s (
     303             :                     GNUNET_TIME_absolute_get_remaining (
     304             :                       fbw->af->start_date.abs_time),
     305             :                     true));
     306             :     }
     307             :   } /* for all wire methods */
     308             : 
     309             :   {
     310             :     struct TMH_EXCHANGES_KeysOperation *kon;
     311             : 
     312         191 :     kon = NULL;
     313         191 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     314             :                 "Processing find operations for `%s'\n",
     315             :                 exchange->url);
     316         191 :     for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head;
     317         363 :          NULL != ko;
     318         172 :          ko = kon)
     319             :     {
     320         172 :       kon = ko->next;
     321         172 :       ko->fc (ko->fc_cls,
     322             :               exchange->keys,
     323             :               exchange);
     324         172 :       TMH_EXCHANGES_keys4exchange_cancel (ko);
     325             :     }
     326             :   }
     327         191 : }
     328             : 
     329             : 
     330             : /**
     331             :  * Function called with information about the wire fees for each wire method.
     332             :  * Stores the wire fees within our internal data structures for later use.
     333             :  *
     334             :  * @param exchange connection to the exchange
     335             :  * @param master_pub public key of the exchange
     336             :  * @param num_methods number of wire methods supported
     337             :  * @param fbm wire fees by method
     338             :  * @return #GNUNET_OK on success
     339             :  */
     340             : static enum GNUNET_GenericReturnValue
     341          24 : process_wire_fees (
     342             :   struct TMH_Exchange *exchange,
     343             :   const struct TALER_MasterPublicKeyP *master_pub,
     344             :   unsigned int num_methods,
     345             :   const struct TALER_EXCHANGE_WireFeesByMethod fbm[static num_methods])
     346          24 : {
     347          48 :   for (unsigned int i = 0; i<num_methods; i++)
     348             :   {
     349          24 :     const char *wire_method = fbm[i].method;
     350          24 :     const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head;
     351             :     struct FeesByWireMethod *f;
     352             :     struct TALER_EXCHANGE_WireAggregateFees *endp;
     353             : 
     354          24 :     for (f = exchange->wire_fees_head; NULL != f; f = f->next)
     355           0 :       if (0 == strcasecmp (wire_method,
     356           0 :                            f->wire_method))
     357           0 :         break;
     358          24 :     if (NULL == f)
     359             :     {
     360          24 :       f = GNUNET_new (struct FeesByWireMethod);
     361          24 :       f->wire_method = GNUNET_strdup (wire_method);
     362          24 :       GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
     363             :                                    exchange->wire_fees_tail,
     364             :                                    f);
     365             :     }
     366          24 :     endp = f->af;
     367          24 :     while ( (NULL != endp) &&
     368           0 :             (NULL != endp->next) )
     369           0 :       endp = endp->next;
     370          24 :     while ( (NULL != endp) &&
     371          24 :             (NULL != fees) &&
     372           0 :             (GNUNET_TIME_timestamp_cmp (fees->start_date,
     373             :                                         <,
     374             :                                         endp->end_date)) )
     375           0 :       fees = fees->next;
     376          24 :     if ( (NULL != endp) &&
     377           0 :          (NULL != fees) &&
     378           0 :          (GNUNET_TIME_timestamp_cmp (fees->start_date,
     379             :                                      !=,
     380             :                                      endp->end_date)) )
     381             :     {
     382             :       /* Hole or overlap in the fee structure, not allowed! */
     383           0 :       GNUNET_break_op (0);
     384           0 :       return GNUNET_SYSERR;
     385             :     }
     386          62 :     while (NULL != fees)
     387             :     {
     388             :       struct TALER_EXCHANGE_WireAggregateFees *af;
     389             : 
     390          38 :       af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
     391          38 :       *af = *fees;
     392          38 :       af->next = NULL;
     393          38 :       if (NULL == endp)
     394          24 :         f->af = af;
     395             :       else
     396          14 :         endp->next = af;
     397          38 :       endp = af;
     398          38 :       fees = fees->next;
     399             :     } /* all fees for this method */
     400             :   } /* for all methods (i) */
     401          24 :   return GNUNET_OK;
     402             : }
     403             : 
     404             : 
     405             : /**
     406             :  * Retry getting keys from the given exchange in the closure.
     407             :  *
     408             :  * @param cls the `struct TMH_Exchange *`
     409             :  */
     410             : static void
     411          31 : retry_exchange (void *cls)
     412             : {
     413          31 :   struct TMH_Exchange *exchange = cls;
     414          31 :   struct GNUNET_DB_EventHeaderP es = {
     415          31 :     .size = ntohs (sizeof (es)),
     416          31 :     .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS)
     417             :   };
     418             : 
     419          31 :   exchange->retry_task = NULL;
     420             :   exchange->retry_delay
     421          31 :     = GNUNET_TIME_randomized_backoff (exchange->retry_delay,
     422             :                                       RETRY_BACKOFF_THRESHOLD);
     423             :   exchange->first_retry
     424          31 :     = GNUNET_TIME_relative_to_absolute (
     425             :         exchange->retry_delay);
     426             : 
     427          31 :   TMH_db->event_notify (TMH_db->cls,
     428             :                         &es,
     429          31 :                         exchange->url,
     430          31 :                         strlen (exchange->url) + 1);
     431          31 : }
     432             : 
     433             : 
     434             : /**
     435             :  * Task to asynchronously return keys operation result to caller.
     436             :  *
     437             :  * @param cls a `struct TMH_EXCHANGES_KeysOperation`
     438             :  */
     439             : static void
     440         167 : return_keys (void *cls)
     441             : {
     442         167 :   struct TMH_EXCHANGES_KeysOperation *fo = cls;
     443         167 :   struct TMH_Exchange *exchange = fo->my_exchange;
     444             : 
     445         167 :   fo->at = NULL;
     446         167 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     447             :               "Returning key data for %s instantly\n",
     448             :               exchange->url);
     449         167 :   process_find_operations (exchange);
     450         167 : }
     451             : 
     452             : 
     453             : struct TMH_EXCHANGES_KeysOperation *
     454         173 : TMH_EXCHANGES_keys4exchange (
     455             :   const char *chosen_exchange,
     456             :   bool force_download,
     457             :   TMH_EXCHANGES_Find2Continuation fc,
     458             :   void *fc_cls)
     459             : {
     460             :   struct TMH_Exchange *exchange;
     461             :   struct TMH_EXCHANGES_KeysOperation *fo;
     462             : 
     463         173 :   if (NULL == TMH_curl_ctx)
     464             :   {
     465           0 :     GNUNET_break (0);
     466           0 :     return NULL;
     467             :   }
     468         173 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     469             :               "Trying to find chosen exchange `%s'\n",
     470             :               chosen_exchange);
     471         173 :   exchange = lookup_exchange (chosen_exchange);
     472         173 :   if (NULL == exchange)
     473             :   {
     474           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     475             :                 "Exchange `%s' not configured\n",
     476             :                 chosen_exchange);
     477           0 :     return NULL;
     478             :   }
     479         173 :   if (! exchange->trusted)
     480             :   {
     481           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     482             :                 "Exchange `%s' not trusted\n",
     483             :                 chosen_exchange);
     484           0 :     return NULL;
     485             :   }
     486         173 :   fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation);
     487         173 :   fo->fc = fc;
     488         173 :   fo->fc_cls = fc_cls;
     489         173 :   fo->my_exchange = exchange;
     490         173 :   GNUNET_CONTAINER_DLL_insert (exchange->keys_head,
     491             :                                exchange->keys_tail,
     492             :                                fo);
     493         173 :   if ( (NULL == exchange->keys) &&
     494           4 :        (! force_download) )
     495             :   {
     496           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     497             :                 "Waiting for `%skeys' already, failing query instantly\n",
     498             :                 exchange->url);
     499           2 :     GNUNET_assert (NULL == fo->at);
     500           2 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     501             :                                        fo);
     502           2 :     return fo;
     503             :   }
     504         171 :   if ( (NULL != exchange->keys) &&
     505         336 :        (! force_download) &&
     506         167 :        (GNUNET_TIME_absolute_is_future (
     507         167 :           exchange->keys->key_data_expiration.abs_time)) )
     508             :   {
     509             :     /* We have a valid reply, immediately return result */
     510         167 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     511             :                 "The exchange `%s' is ready\n",
     512             :                 exchange->url);
     513         167 :     GNUNET_assert (NULL == fo->at);
     514         167 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     515             :                                        fo);
     516         167 :     return fo;
     517             :   }
     518           8 :   if ( (force_download) &&
     519           4 :        (GNUNET_TIME_absolute_is_future (exchange->first_retry)) &&
     520           1 :        (NULL != exchange->keys) )
     521             :   {
     522             :     /* Return results immediately. */
     523           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     524             :                 "Earliest retry is in the future, returning keys now\n");
     525           1 :     fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
     526             :                                        fo);
     527             :     /* *no* return here, we MAY schedule a 'retry_task' in the
     528             :        next block if there isn't one yet */
     529             :   }
     530           4 :   if (NULL == exchange->retry_task)
     531             :     exchange->retry_task
     532           4 :       = GNUNET_SCHEDULER_add_at (exchange->first_retry,
     533             :                                  &retry_exchange,
     534             :                                  exchange);
     535           4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     536             :               "Next %skeys request scheduled for %s\n",
     537             :               exchange->url,
     538             :               GNUNET_TIME_absolute2s (
     539             :                 exchange->first_retry));
     540             :   /* No activity to launch, we are already doing so. */
     541           4 :   return fo;
     542             : }
     543             : 
     544             : 
     545             : void
     546         173 : TMH_EXCHANGES_keys4exchange_cancel (
     547             :   struct TMH_EXCHANGES_KeysOperation *fo)
     548             : {
     549         173 :   struct TMH_Exchange *exchange = fo->my_exchange;
     550             : 
     551         173 :   if (NULL != fo->at)
     552             :   {
     553           3 :     GNUNET_SCHEDULER_cancel (fo->at);
     554           3 :     fo->at = NULL;
     555             :   }
     556         173 :   GNUNET_CONTAINER_DLL_remove (exchange->keys_head,
     557             :                                exchange->keys_tail,
     558             :                                fo);
     559         173 :   GNUNET_free (fo);
     560         173 : }
     561             : 
     562             : 
     563             : /**
     564             :  * Obtain applicable fees for @a exchange and @a wire_method.
     565             :  *
     566             :  * @param exchange the exchange to query
     567             :  * @param now current time
     568             :  * @param wire_method the wire method we want the fees for
     569             :  * @return NULL if we do not have fees for this method yet
     570             :  */
     571             : static const struct FeesByWireMethod *
     572          34 : get_wire_fees (const struct TMH_Exchange *exchange,
     573             :                struct GNUNET_TIME_Timestamp now,
     574             :                const char *wire_method)
     575             : {
     576          34 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     577          34 :        NULL != fbw;
     578           0 :        fbw = fbw->next)
     579             :   {
     580          34 :     if (0 == strcasecmp (fbw->wire_method,
     581             :                          wire_method) )
     582             :     {
     583             :       struct TALER_EXCHANGE_WireAggregateFees *af;
     584             : 
     585             :       /* Advance through list up to current time */
     586          34 :       while ( (NULL != (af = fbw->af)) &&
     587          34 :               (GNUNET_TIME_timestamp_cmp (now,
     588             :                                           >=,
     589             :                                           af->end_date)) )
     590             :       {
     591           0 :         fbw->af = af->next;
     592           0 :         GNUNET_free (af);
     593             :       }
     594          34 :       return fbw;
     595             :     }
     596           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     597             :                 "Exchange supports `%s' as a wire method (but we do not use that one)\n",
     598             :                 fbw->wire_method);
     599             :   }
     600           0 :   return NULL;
     601             : }
     602             : 
     603             : 
     604             : /**
     605             :  * Free @a exchange.
     606             :  *
     607             :  * @param[in] exchange entry to free
     608             :  */
     609             : static void
     610          28 : free_exchange_entry (struct TMH_Exchange *exchange)
     611             : {
     612             :   struct FeesByWireMethod *f;
     613             : 
     614          28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     615             :               "Releasing %s exchange %s\n",
     616             :               exchange->trusted ? "trusted" : "untrusted",
     617             :               exchange->url);
     618          28 :   GNUNET_CONTAINER_DLL_remove (exchange_head,
     619             :                                exchange_tail,
     620             :                                exchange);
     621          52 :   while (NULL != (f = exchange->wire_fees_head))
     622             :   {
     623             :     struct TALER_EXCHANGE_WireAggregateFees *af;
     624             : 
     625          24 :     GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
     626             :                                  exchange->wire_fees_tail,
     627             :                                  f);
     628          62 :     while (NULL != (af = f->af))
     629             :     {
     630          38 :       f->af = af->next;
     631          38 :       GNUNET_free (af);
     632             :     }
     633          24 :     GNUNET_free (f->wire_method);
     634          24 :     GNUNET_free (f);
     635             :   }
     636          28 :   TALER_EXCHANGE_keys_decref (exchange->keys);
     637          28 :   exchange->keys = NULL;
     638          28 :   if (NULL != exchange->retry_task)
     639             :   {
     640           1 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     641           1 :     exchange->retry_task = NULL;
     642             :   }
     643          28 :   GNUNET_assert (NULL == exchange->keys_head);
     644          28 :   GNUNET_assert (NULL == exchange->keys_tail);
     645          28 :   GNUNET_free (exchange->currency);
     646          28 :   GNUNET_free (exchange->url);
     647          28 :   GNUNET_free (exchange);
     648          28 : }
     649             : 
     650             : 
     651             : enum GNUNET_GenericReturnValue
     652          34 : TMH_EXCHANGES_lookup_wire_fee (
     653             :   const struct TMH_Exchange *exchange,
     654             :   const char *wire_method,
     655             :   struct TALER_Amount *wire_fee)
     656             : {
     657             :   const struct FeesByWireMethod *fbm;
     658             :   const struct TALER_EXCHANGE_WireAggregateFees *af;
     659             : 
     660          34 :   fbm = get_wire_fees (exchange,
     661             :                        GNUNET_TIME_timestamp_get (),
     662             :                        wire_method);
     663          34 :   if (NULL == fbm)
     664           0 :     return GNUNET_NO;
     665          34 :   af = fbm->af;
     666          34 :   *wire_fee = af->fees.wire;
     667          34 :   return GNUNET_OK;
     668             : }
     669             : 
     670             : 
     671             : enum GNUNET_GenericReturnValue
     672          96 : TMH_exchange_check_debit (
     673             :   const char *instance_id,
     674             :   const struct TMH_Exchange *exchange,
     675             :   const struct TMH_WireMethod *wm,
     676             :   struct TALER_Amount *max_amount)
     677             : {
     678          96 :   const struct TALER_EXCHANGE_Keys *keys = exchange->keys;
     679          96 :   bool have_kyc = false;
     680          96 :   bool no_access_token = true;
     681             : 
     682          96 :   if (NULL == keys)
     683           0 :     return GNUNET_SYSERR;
     684          96 :   if (0 != strcasecmp (keys->currency,
     685          96 :                        max_amount->currency))
     686             :   {
     687           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     688             :                 "Currency mismatch: exchange %s uses %s, we need %s\n",
     689             :                 exchange->url,
     690             :                 keys->currency,
     691             :                 max_amount->currency);
     692           0 :     return GNUNET_SYSERR;
     693             :   }
     694             :   {
     695             :     struct TALER_NormalizedPayto np;
     696             :     bool account_ok;
     697             : 
     698          96 :     np = TALER_payto_normalize (wm->payto_uri);
     699          96 :     account_ok = TALER_EXCHANGE_keys_test_account_allowed (keys,
     700             :                                                            false,
     701             :                                                            np);
     702          96 :     GNUNET_free (np.normalized_payto);
     703          96 :     if (! account_ok)
     704           0 :       return GNUNET_NO;
     705             :   }
     706          96 :   if (! keys->kyc_enabled)
     707          92 :     return GNUNET_YES;
     708             : 
     709             :   {
     710           4 :     json_t *jlimits = NULL;
     711             :     enum GNUNET_DB_QueryStatus qs;
     712             : 
     713           4 :     qs = TMH_db->get_kyc_limits (TMH_db->cls,
     714             :                                  wm->payto_uri,
     715             :                                  instance_id,
     716           4 :                                  exchange->url,
     717             :                                  &have_kyc,
     718             :                                  &no_access_token,
     719             :                                  &jlimits);
     720           4 :     GNUNET_break (qs >= 0);
     721           4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     722             :                 "get_kyc_limits for %s at %s returned %s/%s\n",
     723             :                 wm->payto_uri.full_payto,
     724             :                 exchange->url,
     725             :                 have_kyc ? "KYC OK" : "KYC missing",
     726             :                 NULL == jlimits ? "default limits" : "custom limits");
     727           4 :     if ( (qs > 0) &&
     728           4 :          (NULL != jlimits) )
     729             :     {
     730             :       json_t *jlimit;
     731             :       size_t idx;
     732             :       struct TALER_Amount kyc_limit;
     733           4 :       bool unlimited = true;
     734             : 
     735           8 :       json_array_foreach (jlimits, idx, jlimit)
     736             :       {
     737             :         enum TALER_KYCLOGIC_KycTriggerEvent ot;
     738             :         struct TALER_Amount threshold;
     739           4 :         bool soft_limit = false;
     740             :         struct GNUNET_JSON_Specification spec[] = {
     741           4 :           TALER_JSON_spec_kycte ("operation_type",
     742             :                                  &ot),
     743           4 :           TALER_JSON_spec_amount_any ("threshold",
     744             :                                       &threshold),
     745           4 :           GNUNET_JSON_spec_mark_optional (
     746             :             GNUNET_JSON_spec_bool ("soft_limit",
     747             :                                    &soft_limit),
     748             :             NULL),
     749           4 :           GNUNET_JSON_spec_end ()
     750             :         };
     751             : 
     752           4 :         if (GNUNET_OK !=
     753           4 :             GNUNET_JSON_parse (jlimit,
     754             :                                spec,
     755             :                                NULL, NULL))
     756             :         {
     757           0 :           GNUNET_break (0);
     758           4 :           continue;
     759             :         }
     760           4 :         if (soft_limit)
     761           4 :           continue;
     762           0 :         if ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT != ot) &&
     763           0 :              (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION != ot) )
     764           0 :           continue;
     765           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     766             :                     "KYC rule %u with limit %s applies\n",
     767             :                     (unsigned int) idx,
     768             :                     TALER_amount2s (&threshold));
     769           0 :         if (unlimited)
     770             :         {
     771           0 :           unlimited = false;
     772           0 :           kyc_limit = threshold;
     773             :         }
     774             :         else
     775             :         {
     776           0 :           TALER_amount_min (&kyc_limit,
     777             :                             &kyc_limit,
     778             :                             &threshold);
     779             :         }
     780             :       }
     781           4 :       json_decref (jlimits);
     782             :       /* We had custom rules, do not evaluate default rules */
     783           4 :       if (! unlimited)
     784           0 :         TALER_amount_min (max_amount,
     785             :                           max_amount,
     786             :                           &kyc_limit);
     787           4 :       return GNUNET_YES;
     788             :     } /* END of if qs > 0, NULL != jlimits */
     789             :   }
     790             : 
     791             :   /* Check zero limits *only* if we did no KYC process at all yet.
     792             :      Because if we did, there is at least a chance that those have
     793             :      been lifted. */
     794           0 :   if ( (no_access_token) ||
     795           0 :        ( (! have_kyc) &&
     796           0 :          (TALER_EXCHANGE_keys_evaluate_zero_limits (
     797             :             keys,
     798           0 :             TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     799           0 :           TALER_EXCHANGE_keys_evaluate_zero_limits (
     800             :             keys,
     801             :             TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) )
     802             :   {
     803           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     804             :                 "KYC requirements of %s not satisfied\n",
     805             :                 exchange->url);
     806           0 :     GNUNET_assert (GNUNET_OK ==
     807             :                    TALER_amount_set_zero (
     808             :                      max_amount->currency,
     809             :                      max_amount));
     810           0 :     return GNUNET_YES;
     811             :   }
     812             :   /* In any case, abide by hard limits (unless we have custom rules). */
     813           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     814             :               "Evaluating default hard limits of %s\n",
     815             :               exchange->url);
     816           0 :   TALER_EXCHANGE_keys_evaluate_hard_limits (
     817             :     keys,
     818             :     TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
     819             :     max_amount);
     820           0 :   TALER_EXCHANGE_keys_evaluate_hard_limits (
     821             :     keys,
     822             :     TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
     823             :     max_amount);
     824           0 :   if (TALER_EXCHANGE_keys_evaluate_zero_limits (
     825             :         keys,
     826           0 :         TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
     827           0 :       TALER_EXCHANGE_keys_evaluate_zero_limits (
     828             :         keys,
     829             :         TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION))
     830             :   {
     831           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     832             :                 "Operation is zero-limited by default\n");
     833           0 :     GNUNET_assert (GNUNET_OK ==
     834             :                    TALER_amount_set_zero (max_amount->currency,
     835             :                                           max_amount));
     836             :   }
     837           0 :   return GNUNET_YES;
     838             : }
     839             : 
     840             : 
     841             : void
     842          77 : TMH_exchange_get_trusted (TMH_ExchangeCallback cb,
     843             :                           void *cb_cls)
     844             : {
     845          77 :   for (const struct TMH_Exchange *exchange = exchange_head;
     846         231 :        NULL != exchange;
     847         154 :        exchange = exchange->next)
     848             :   {
     849         154 :     if (! exchange->trusted)
     850             :     {
     851           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     852             :                   "Exchange %s not trusted, skipping!\n",
     853             :                   exchange->url);
     854           0 :       continue;
     855             :     }
     856         154 :     cb (cb_cls,
     857         154 :         exchange->url,
     858             :         exchange);
     859             :   }
     860          77 : }
     861             : 
     862             : 
     863             : bool
     864         138 : TMH_test_exchange_configured_for_currency (
     865             :   const char *currency)
     866             : {
     867         138 :   for (const struct TMH_Exchange *exchange = exchange_head;
     868         304 :        NULL != exchange;
     869         166 :        exchange = exchange->next)
     870             :   {
     871         263 :     if (! exchange->trusted)
     872           0 :       continue;
     873         263 :     if (NULL == exchange->currency)
     874           0 :       continue;
     875         263 :     if (0 == strcmp (currency,
     876         263 :                      exchange->currency))
     877          97 :       return true;
     878             :   }
     879          41 :   return false;
     880             : }
     881             : 
     882             : 
     883             : /**
     884             :  * (Re)load of keys from DB.
     885             :  *
     886             :  * @param exchange exchange to reload keys of
     887             :  */
     888             : static void
     889          42 : reload_exchange_keys (struct TMH_Exchange *exchange)
     890             : {
     891             :   enum GNUNET_DB_QueryStatus qs;
     892             :   struct TALER_EXCHANGE_Keys *keys;
     893             :   struct GNUNET_TIME_Absolute first_retry;
     894             : 
     895          42 :   qs = TMH_db->select_exchange_keys (TMH_db->cls,
     896          42 :                                      exchange->url,
     897             :                                      &first_retry,
     898             :                                      &keys);
     899          42 :   if (qs < 0)
     900             :   {
     901           0 :     GNUNET_break (0);
     902          18 :     return;
     903             :   }
     904          42 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     905             :   {
     906          18 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     907             :                 "No keys yet for `%s'\n",
     908             :                 exchange->url);
     909          18 :     return;
     910             :   }
     911          24 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     912             :               "Loading latest keys of `%s' from database\n",
     913             :               exchange->url);
     914          24 :   exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
     915          24 :   exchange->first_retry = first_retry;
     916          24 :   if (NULL == exchange->currency)
     917           0 :     exchange->currency = GNUNET_strdup (keys->currency);
     918          24 :   if (0 != strcmp (keys->currency,
     919          24 :                    exchange->currency))
     920             :   {
     921           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     922             :                 "/keys cached in our database are for currency `%s', but we expected `%s'\n",
     923             :                 keys->currency,
     924             :                 exchange->currency);
     925           0 :     return;
     926             :   }
     927          24 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     928             :               "Loaded /keys from database with %u accounts\n",
     929             :               keys->accounts_len);
     930          24 :   if (GNUNET_OK !=
     931          24 :       process_wire_fees (exchange,
     932          24 :                          &keys->master_pub,
     933          24 :                          keys->fees_len,
     934          24 :                          keys->fees))
     935             :   {
     936             :     /* invalid wire fee specification given */
     937           0 :     GNUNET_break_op (0);
     938             :     /* but: we can continue anyway, things may just not
     939             :        work, but probably better than to not keep going. */
     940           0 :     return;
     941             :   }
     942             : 
     943          24 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     944             :               "Reloaded /keys of %s from database\n",
     945             :               exchange->url);
     946          24 :   TALER_EXCHANGE_keys_decref (exchange->keys);
     947          24 :   exchange->keys = keys;
     948          24 :   if ( (exchange->trusted) &&
     949          24 :        (0 != GNUNET_memcmp (&exchange->master_pub,
     950             :                             &keys->master_pub)) )
     951             :   {
     952             :     /* master pub differs => do not trust the exchange (without auditor) */
     953           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     954             :                 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
     955             :                 exchange->url);
     956           0 :     exchange->trusted = false;
     957             :   }
     958          24 :   if (! exchange->trusted)
     959             :   {
     960           0 :     exchange->master_pub = keys->master_pub;
     961           0 :     for (struct TMH_Exchange *e = exchange_head;
     962           0 :          NULL != e;
     963           0 :          e = e->next)
     964             :     {
     965           0 :       if (e == exchange)
     966           0 :         continue;
     967           0 :       if (! e->trusted)
     968           0 :         continue;
     969           0 :       if (0 ==
     970           0 :           GNUNET_memcmp (&e->master_pub,
     971             :                          &exchange->master_pub))
     972           0 :         exchange->trusted = true; /* same exchange, different URL => trust applies */
     973             :     }
     974             :   }
     975             : 
     976          24 :   process_find_operations (exchange);
     977             : }
     978             : 
     979             : 
     980             : /**
     981             :  * Function called on each configuration section. Finds sections
     982             :  * about exchanges, parses the entries.
     983             :  *
     984             :  * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
     985             :  * @param section name of the section
     986             :  */
     987             : static void
     988         545 : accept_exchanges (void *cls,
     989             :                   const char *section)
     990             : {
     991         545 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
     992             :   char *url;
     993             :   char *mks;
     994             :   struct TMH_Exchange *exchange;
     995             :   char *currency;
     996             : 
     997         545 :   if (GNUNET_SYSERR == trusted_exchange_count)
     998         517 :     return;
     999         545 :   if (0 != strncasecmp (section,
    1000             :                         "merchant-exchange-",
    1001             :                         strlen ("merchant-exchange-")))
    1002         503 :     return;
    1003          42 :   if (GNUNET_YES ==
    1004          42 :       GNUNET_CONFIGURATION_get_value_yesno (cfg,
    1005             :                                             section,
    1006             :                                             "DISABLED"))
    1007          14 :     return;
    1008          28 :   if (GNUNET_OK !=
    1009          28 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1010             :                                              section,
    1011             :                                              "EXCHANGE_BASE_URL",
    1012             :                                              &url))
    1013             :   {
    1014           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1015             :                                section,
    1016             :                                "EXCHANGE_BASE_URL");
    1017           0 :     return;
    1018             :   }
    1019          28 :   exchange = lookup_exchange (url);
    1020          28 :   if (NULL != exchange)
    1021             :   {
    1022           0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1023             :                                section,
    1024             :                                "EXCHANGE_BASE_URL",
    1025             :                                "same base URL specified again");
    1026           0 :     GNUNET_free (url);
    1027           0 :     return;
    1028             :   }
    1029          28 :   if (GNUNET_OK !=
    1030          28 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1031             :                                              section,
    1032             :                                              "CURRENCY",
    1033             :                                              &currency))
    1034             :   {
    1035           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1036             :                                section,
    1037             :                                "CURRENCY");
    1038           0 :     GNUNET_free (url);
    1039           0 :     return;
    1040             :   }
    1041          28 :   exchange = GNUNET_new (struct TMH_Exchange);
    1042          28 :   exchange->url = url;
    1043          28 :   exchange->currency = currency;
    1044          28 :   GNUNET_CONTAINER_DLL_insert (exchange_head,
    1045             :                                exchange_tail,
    1046             :                                exchange);
    1047          28 :   if (GNUNET_OK ==
    1048          28 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1049             :                                              section,
    1050             :                                              "MASTER_KEY",
    1051             :                                              &mks))
    1052             :   {
    1053          28 :     if (GNUNET_OK ==
    1054          28 :         GNUNET_CRYPTO_eddsa_public_key_from_string (
    1055             :           mks,
    1056             :           strlen (mks),
    1057             :           &exchange->master_pub.eddsa_pub))
    1058             :     {
    1059          28 :       exchange->trusted = true;
    1060          28 :       trusted_exchange_count++;
    1061             :     }
    1062             :     else
    1063             :     {
    1064           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1065             :                                  section,
    1066             :                                  "MASTER_KEY",
    1067             :                                  "malformed EdDSA key");
    1068             :     }
    1069          28 :     GNUNET_free (mks);
    1070             :   }
    1071             :   else
    1072             :   {
    1073           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1074             :                 "MASTER_KEY missing in section '%s', not trusting exchange\n",
    1075             :                 section);
    1076             :   }
    1077          28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1078             :               "Setup exchange %s as %s\n",
    1079             :               exchange->url,
    1080             :               exchange->trusted ? "trusted" : "untrusted");
    1081          28 :   reload_exchange_keys (exchange);
    1082          28 :   if (NULL != exchange->retry_task)
    1083             :   {
    1084           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1085             :                 "Exchange at `%s' configured in multiple configuration sections (see `%s')!\n",
    1086             :                 exchange->url,
    1087             :                 section);
    1088           0 :     trusted_exchange_count = GNUNET_SYSERR;
    1089           0 :     return;
    1090             :   }
    1091             :   exchange->retry_task
    1092          28 :     = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1093             :                                 exchange);
    1094             : }
    1095             : 
    1096             : 
    1097             : /**
    1098             :  * Trigger (re)loading of keys from DB.
    1099             :  *
    1100             :  * @param cls NULL
    1101             :  * @param extra base URL of the exchange that changed
    1102             :  * @param extra_len number of bytes in @a extra
    1103             :  */
    1104             : static void
    1105          14 : update_exchange_keys (void *cls,
    1106             :                       const void *extra,
    1107             :                       size_t extra_len)
    1108             : {
    1109          14 :   const char *url = extra;
    1110             :   struct TMH_Exchange *exchange;
    1111             : 
    1112          14 :   if ( (NULL == extra) ||
    1113             :        (0 == extra_len) )
    1114             :   {
    1115           0 :     GNUNET_break (0);
    1116           0 :     return;
    1117             :   }
    1118          14 :   if ('\0' != url[extra_len - 1])
    1119             :   {
    1120           0 :     GNUNET_break (0);
    1121           0 :     return;
    1122             :   }
    1123          14 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1124             :               "Received keys change notification: reload `%s'\n",
    1125             :               url);
    1126          14 :   exchange = lookup_exchange (url);
    1127          14 :   GNUNET_break (NULL != exchange);
    1128          14 :   if (NULL != exchange)
    1129          14 :     reload_exchange_keys (exchange);
    1130             : }
    1131             : 
    1132             : 
    1133             : bool
    1134          34 : TMH_EXCHANGES_is_below_limit (
    1135             :   const struct TALER_EXCHANGE_Keys *keys,
    1136             :   enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
    1137             :   const struct TALER_Amount *amount)
    1138             : {
    1139          34 :   if (NULL == keys)
    1140             :   {
    1141             :     /* should only be called after we have keys! */
    1142           0 :     GNUNET_break (0);
    1143           0 :     return false;
    1144             :   }
    1145          34 :   for (unsigned int i = 0; i<keys->hard_limits_length; i++)
    1146             :   {
    1147           0 :     const struct TALER_EXCHANGE_AccountLimit *al
    1148           0 :       = &keys->hard_limits[i];
    1149             : 
    1150           0 :     if (operation_type != al->operation_type)
    1151           0 :       continue;
    1152           0 :     if (-1 ==
    1153           0 :         TALER_amount_cmp (&al->threshold,
    1154             :                           amount))
    1155             :       /* -1: threshold < amount */
    1156           0 :       return false;
    1157             :   }
    1158          34 :   return true;
    1159             : }
    1160             : 
    1161             : 
    1162             : void
    1163           6 : TMH_EXCHANGES_get_limit (
    1164             :   const char *exchange_url,
    1165             :   enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
    1166             :   struct TALER_Amount *amount)
    1167             : {
    1168             :   struct TMH_Exchange *exchange;
    1169             :   const struct TALER_EXCHANGE_Keys *keys;
    1170             : 
    1171           6 :   exchange = lookup_exchange (exchange_url);
    1172           6 :   if ( (NULL == exchange) ||
    1173           6 :        (NULL == (keys = exchange->keys)) )
    1174             :   {
    1175           0 :     GNUNET_assert (GNUNET_OK ==
    1176             :                    TALER_amount_set_zero (
    1177             :                      amount->currency,
    1178             :                      amount));
    1179           0 :     return;
    1180             :   }
    1181           6 :   for (unsigned int i = 0; i<keys->hard_limits_length; i++)
    1182             :   {
    1183           0 :     const struct TALER_EXCHANGE_AccountLimit *al
    1184           0 :       = &keys->hard_limits[i];
    1185             : 
    1186           0 :     if (operation_type != al->operation_type)
    1187           0 :       continue;
    1188           0 :     TALER_amount_min (amount,
    1189             :                       amount,
    1190             :                       &al->threshold);
    1191             :   }
    1192             : }
    1193             : 
    1194             : 
    1195             : enum GNUNET_GenericReturnValue
    1196          14 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    1197             : {
    1198             :   /* get exchanges from the merchant configuration and try to connect to them */
    1199             :   {
    1200          14 :     struct GNUNET_DB_EventHeaderP es = {
    1201          14 :       .size = ntohs (sizeof (es)),
    1202          14 :       .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
    1203             :     };
    1204             : 
    1205          14 :     GNUNET_assert (NULL == keys_eh);
    1206          28 :     keys_eh = TMH_db->event_listen (TMH_db->cls,
    1207             :                                     &es,
    1208          14 :                                     GNUNET_TIME_UNIT_FOREVER_REL,
    1209             :                                     &update_exchange_keys,
    1210             :                                     NULL);
    1211             :   }
    1212          14 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1213             :                                          &accept_exchanges,
    1214             :                                          (void *) cfg);
    1215             :   /* build JSON with list of trusted exchanges (will be included in contracts) */
    1216          14 :   return trusted_exchange_count;
    1217             : }
    1218             : 
    1219             : 
    1220             : void
    1221          14 : TMH_EXCHANGES_done ()
    1222             : {
    1223          14 :   if (NULL != keys_eh)
    1224             :   {
    1225          14 :     TMH_db->event_listen_cancel (keys_eh);
    1226          14 :     keys_eh = NULL;
    1227             :   }
    1228          42 :   while (NULL != exchange_head)
    1229          28 :     free_exchange_entry (exchange_head);
    1230          14 : }
    1231             : 
    1232             : 
    1233             : /* end of taler-merchant-httpd_exchanges.c */

Generated by: LCOV version 1.16