merchant

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

taler-merchant-httpd_exchanges.c (35141B)


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