LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_exchanges.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 68.3 % 426 291
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 23 23

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

Generated by: LCOV version 2.0-1