merchant

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

taler-merchant-httpd_exchanges.c (34448B)


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