merchant

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

taler-merchant-httpd_exchanges.c (34380B)


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