merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_exchanges.c (34719B)


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