exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

fakebank.c (14163B)


      1 /*
      2   This file is part of TALER
      3   (C) 2016-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU General Public License
      7   as published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file bank-lib/fakebank.c
     21  * @brief library that fakes being a Taler bank for testcases
     22  * @author Christian Grothoff <christian@grothoff.org>
     23  * @defgroup request Request handling routines
     24  */
     25 #include "taler/platform.h"
     26 #include <pthread.h>
     27 #include <poll.h>
     28 #ifdef __linux__
     29 #include <sys/eventfd.h>
     30 #endif
     31 #include "taler/taler_fakebank_lib.h"
     32 #include "taler/taler_bank_service.h"
     33 #include "taler/taler_mhd_lib.h"
     34 #include <gnunet/gnunet_mhd_compat.h>
     35 #include "fakebank.h"
     36 #include "fakebank_bank.h"
     37 #include "fakebank_common_lp.h"
     38 #include "fakebank_tbi.h"
     39 
     40 
     41 /**
     42  * Function called whenever MHD is done with a request.  If the
     43  * request was a POST, we may have stored a `struct Buffer *` in the
     44  * @a con_cls that might still need to be cleaned up.  Call the
     45  * respective function to free the memory.
     46  *
     47  * @param cls a `struct TALER_FAKEBANK_Handle *`
     48  * @param connection connection handle
     49  * @param con_cls a `struct ConnectionContext *`
     50  *        the #MHD_AccessHandlerCallback
     51  * @param toe reason for request termination
     52  * @see #MHD_OPTION_NOTIFY_COMPLETED
     53  * @ingroup request
     54  */
     55 static void
     56 handle_mhd_completion_callback (void *cls,
     57                                 struct MHD_Connection *connection,
     58                                 void **con_cls,
     59                                 enum MHD_RequestTerminationCode toe)
     60 {
     61   struct TALER_FAKEBANK_Handle *h = cls;
     62   struct ConnectionContext *cc = *con_cls;
     63 
     64   (void) h;
     65   (void) connection;
     66   (void) toe;
     67   if (NULL == cc)
     68     return;
     69   cc->ctx_cleaner (cc->ctx);
     70   GNUNET_free (cc);
     71 }
     72 
     73 
     74 /**
     75  * Handle incoming HTTP request.
     76  *
     77  * @param cls a `struct TALER_FAKEBANK_Handle`
     78  * @param connection the connection
     79  * @param url the requested url
     80  * @param method the method (POST, GET, ...)
     81  * @param version HTTP version (ignored)
     82  * @param upload_data request data
     83  * @param upload_data_size size of @a upload_data in bytes
     84  * @param con_cls closure for request
     85  * @return MHD result code
     86  */
     87 static MHD_RESULT
     88 handle_mhd_request (void *cls,
     89                     struct MHD_Connection *connection,
     90                     const char *url,
     91                     const char *method,
     92                     const char *version,
     93                     const char *upload_data,
     94                     size_t *upload_data_size,
     95                     void **con_cls)
     96 {
     97   struct TALER_FAKEBANK_Handle *h = cls;
     98 
     99   (void) version;
    100   if (0 == strncmp (url,
    101                     "/taler-integration/",
    102                     strlen ("/taler-integration/")))
    103   {
    104     url += strlen ("/taler-integration");
    105     return TALER_FAKEBANK_tbi_main_ (h,
    106                                      connection,
    107                                      url,
    108                                      method,
    109                                      upload_data,
    110                                      upload_data_size,
    111                                      con_cls);
    112   }
    113   return TALER_FAKEBANK_bank_main_ (h,
    114                                     connection,
    115                                     url,
    116                                     method,
    117                                     upload_data,
    118                                     upload_data_size,
    119                                     con_cls);
    120 }
    121 
    122 
    123 #if EPOLL_SUPPORT
    124 /**
    125  * Schedule MHD.  This function should be called initially when an
    126  * MHD is first getting its client socket, and will then automatically
    127  * always be called later whenever there is work to be done.
    128  *
    129  * @param h fakebank handle to schedule MHD for
    130  */
    131 static void
    132 schedule_httpd (struct TALER_FAKEBANK_Handle *h)
    133 {
    134   int haveto;
    135   MHD_UNSIGNED_LONG_LONG timeout;
    136   struct GNUNET_TIME_Relative tv;
    137 
    138   GNUNET_assert (-1 != h->mhd_fd);
    139   haveto = MHD_get_timeout (h->mhd_bank,
    140                             &timeout);
    141   if (MHD_YES == haveto)
    142     tv.rel_value_us = (uint64_t) timeout * 1000LL;
    143   else
    144     tv = GNUNET_TIME_UNIT_FOREVER_REL;
    145   if (NULL != h->mhd_task)
    146     GNUNET_SCHEDULER_cancel (h->mhd_task);
    147   h->mhd_task =
    148     GNUNET_SCHEDULER_add_read_net (tv,
    149                                    h->mhd_rfd,
    150                                    &TALER_FAKEBANK_run_mhd_,
    151                                    h);
    152 }
    153 
    154 
    155 #else
    156 /**
    157  * Schedule MHD.  This function should be called initially when an
    158  * MHD is first getting its client socket, and will then automatically
    159  * always be called later whenever there is work to be done.
    160  *
    161  * @param h fakebank handle to schedule MHD for
    162  */
    163 static void
    164 schedule_httpd (struct TALER_FAKEBANK_Handle *h)
    165 {
    166   fd_set rs;
    167   fd_set ws;
    168   fd_set es;
    169   struct GNUNET_NETWORK_FDSet *wrs;
    170   struct GNUNET_NETWORK_FDSet *wws;
    171   int max;
    172   int haveto;
    173   MHD_UNSIGNED_LONG_LONG timeout;
    174   struct GNUNET_TIME_Relative tv;
    175 
    176 #ifdef __linux__
    177   GNUNET_assert (-1 == h->lp_event);
    178 #else
    179   GNUNET_assert (-1 == h->lp_event_in);
    180   GNUNET_assert (-1 == h->lp_event_out);
    181 #endif
    182   FD_ZERO (&rs);
    183   FD_ZERO (&ws);
    184   FD_ZERO (&es);
    185   max = -1;
    186   if (MHD_YES != MHD_get_fdset (h->mhd_bank,
    187                                 &rs,
    188                                 &ws,
    189                                 &es,
    190                                 &max))
    191   {
    192     GNUNET_assert (0);
    193     return;
    194   }
    195   haveto = MHD_get_timeout (h->mhd_bank,
    196                             &timeout);
    197   if (MHD_YES == haveto)
    198     tv.rel_value_us = (uint64_t) timeout * 1000LL;
    199   else
    200     tv = GNUNET_TIME_UNIT_FOREVER_REL;
    201   if (-1 != max)
    202   {
    203     wrs = GNUNET_NETWORK_fdset_create ();
    204     wws = GNUNET_NETWORK_fdset_create ();
    205     GNUNET_NETWORK_fdset_copy_native (wrs,
    206                                       &rs,
    207                                       max + 1);
    208     GNUNET_NETWORK_fdset_copy_native (wws,
    209                                       &ws,
    210                                       max + 1);
    211   }
    212   else
    213   {
    214     wrs = NULL;
    215     wws = NULL;
    216   }
    217   if (NULL != h->mhd_task)
    218     GNUNET_SCHEDULER_cancel (h->mhd_task);
    219   h->mhd_task =
    220     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
    221                                  tv,
    222                                  wrs,
    223                                  wws,
    224                                  &TALER_FAKEBANK_run_mhd_,
    225                                  h);
    226   if (NULL != wrs)
    227     GNUNET_NETWORK_fdset_destroy (wrs);
    228   if (NULL != wws)
    229     GNUNET_NETWORK_fdset_destroy (wws);
    230 }
    231 
    232 
    233 #endif
    234 
    235 
    236 /**
    237  * Task run whenever HTTP server operations are pending.
    238  *
    239  * @param cls the `struct TALER_FAKEBANK_Handle`
    240  */
    241 void
    242 TALER_FAKEBANK_run_mhd_ (void *cls)
    243 {
    244   struct TALER_FAKEBANK_Handle *h = cls;
    245 
    246   h->mhd_task = NULL;
    247   h->mhd_again = true;
    248   while (h->mhd_again)
    249   {
    250     h->mhd_again = false;
    251     GNUNET_assert (MHD_YES ==
    252                    MHD_run (h->mhd_bank));
    253   }
    254 #ifdef __linux__
    255   GNUNET_assert (-1 == h->lp_event);
    256 #else
    257   GNUNET_assert (-1 == h->lp_event_in);
    258   GNUNET_assert (-1 == h->lp_event_out);
    259 #endif
    260   schedule_httpd (h);
    261 }
    262 
    263 
    264 struct TALER_FAKEBANK_Handle *
    265 TALER_FAKEBANK_start (uint16_t port,
    266                       const char *currency)
    267 {
    268   return TALER_FAKEBANK_start2 (port,
    269                                 currency,
    270                                 65536, /* RAM limit */
    271                                 1);
    272 }
    273 
    274 
    275 struct TALER_FAKEBANK_Handle *
    276 TALER_FAKEBANK_start2 (uint16_t port,
    277                        const char *currency,
    278                        uint64_t ram_limit,
    279                        unsigned int num_threads)
    280 {
    281   struct TALER_Amount zero;
    282 
    283   if (GNUNET_OK !=
    284       TALER_amount_set_zero (currency,
    285                              &zero))
    286   {
    287     GNUNET_break (0);
    288     return NULL;
    289   }
    290   return TALER_FAKEBANK_start3 ("localhost",
    291                                 port,
    292                                 NULL,
    293                                 currency,
    294                                 ram_limit,
    295                                 num_threads,
    296                                 &zero);
    297 }
    298 
    299 
    300 struct TALER_FAKEBANK_Handle *
    301 TALER_FAKEBANK_start3 (const char *hostname,
    302                        uint16_t port,
    303                        const char *exchange_url,
    304                        const char *currency,
    305                        uint64_t ram_limit,
    306                        unsigned int num_threads,
    307                        const struct TALER_Amount *signup_bonus)
    308 {
    309   struct TALER_FAKEBANK_Handle *h;
    310 
    311   if (SIZE_MAX / sizeof (struct Transaction *) < ram_limit)
    312   {
    313     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    314                 "This CPU architecture does not support keeping %llu transactions in RAM\n",
    315                 (unsigned long long) ram_limit);
    316     return NULL;
    317   }
    318   GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
    319   if (0 != strcmp (signup_bonus->currency,
    320                    currency))
    321   {
    322     GNUNET_break (0);
    323     return NULL;
    324   }
    325   h = GNUNET_new (struct TALER_FAKEBANK_Handle);
    326   h->signup_bonus = *signup_bonus;
    327   if (NULL != exchange_url)
    328     h->exchange_url = GNUNET_strdup (exchange_url);
    329 #ifdef __linux__
    330   h->lp_event = -1;
    331 #else
    332   h->lp_event_in = -1;
    333   h->lp_event_out = -1;
    334 #endif
    335 #if EPOLL_SUPPORT
    336   h->mhd_fd = -1;
    337 #endif
    338   h->port = port;
    339   h->ram_limit = ram_limit;
    340   h->serial_counter = 0;
    341   GNUNET_assert (0 ==
    342                  pthread_mutex_init (&h->accounts_lock,
    343                                      NULL));
    344   GNUNET_assert (0 ==
    345                  pthread_mutex_init (&h->rpubs_lock,
    346                                      NULL));
    347   GNUNET_assert (0 ==
    348                  pthread_mutex_init (&h->uuid_map_lock,
    349                                      NULL));
    350   GNUNET_assert (0 ==
    351                  pthread_mutex_init (&h->big_lock,
    352                                      NULL));
    353   h->transactions
    354     = GNUNET_malloc_large (sizeof (struct Transaction *)
    355                            * ram_limit);
    356   if (NULL == h->transactions)
    357   {
    358     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    359                          "malloc");
    360     TALER_FAKEBANK_stop (h);
    361     return NULL;
    362   }
    363   h->accounts = GNUNET_CONTAINER_multihashmap_create (128,
    364                                                       GNUNET_NO);
    365   h->uuid_map = GNUNET_CONTAINER_multihashmap_create (ram_limit * 4 / 3,
    366                                                       GNUNET_YES);
    367   if (NULL == h->uuid_map)
    368   {
    369     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    370                          "malloc");
    371     TALER_FAKEBANK_stop (h);
    372     return NULL;
    373   }
    374   h->rpubs = GNUNET_CONTAINER_multipeermap_create (ram_limit * 4 / 3,
    375                                                    GNUNET_NO);
    376   if (NULL == h->rpubs)
    377   {
    378     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    379                          "malloc");
    380     TALER_FAKEBANK_stop (h);
    381     return NULL;
    382   }
    383   h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
    384   h->currency = GNUNET_strdup (currency);
    385   h->hostname = GNUNET_strdup (hostname);
    386   GNUNET_asprintf (&h->my_baseurl,
    387                    "http://%s:%u/",
    388                    h->hostname,
    389                    (unsigned int) port);
    390   if (0 == num_threads)
    391   {
    392     h->mhd_bank = MHD_start_daemon (
    393       MHD_USE_DEBUG
    394 #if EPOLL_SUPPORT
    395       | MHD_USE_EPOLL
    396 #endif
    397       | MHD_USE_DUAL_STACK
    398       | MHD_ALLOW_SUSPEND_RESUME,
    399       port,
    400       NULL, NULL,
    401       &handle_mhd_request, h,
    402       MHD_OPTION_NOTIFY_COMPLETED,
    403       &handle_mhd_completion_callback, h,
    404       MHD_OPTION_LISTEN_BACKLOG_SIZE,
    405       (unsigned int) 1024,
    406       MHD_OPTION_CONNECTION_LIMIT,
    407       (unsigned int) 65536,
    408       MHD_OPTION_END);
    409     if (NULL == h->mhd_bank)
    410     {
    411       TALER_FAKEBANK_stop (h);
    412       return NULL;
    413     }
    414 #if EPOLL_SUPPORT
    415     h->mhd_fd = MHD_get_daemon_info (h->mhd_bank,
    416                                      MHD_DAEMON_INFO_EPOLL_FD)->epoll_fd;
    417     h->mhd_rfd = GNUNET_NETWORK_socket_box_native (h->mhd_fd);
    418 #endif
    419     schedule_httpd (h);
    420   }
    421   else
    422   {
    423 #ifdef __linux__
    424     h->lp_event = eventfd (0,
    425                            EFD_CLOEXEC);
    426     if (-1 == h->lp_event)
    427     {
    428       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    429                            "eventfd");
    430       TALER_FAKEBANK_stop (h);
    431       return NULL;
    432     }
    433 #else
    434     {
    435       int pipefd[2];
    436 
    437       if (0 != pipe (pipefd))
    438       {
    439         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    440                              "pipe");
    441         TALER_FAKEBANK_stop (h);
    442         return NULL;
    443       }
    444       h->lp_event_out = pipefd[0];
    445       h->lp_event_in = pipefd[1];
    446     }
    447 #endif
    448     if (0 !=
    449         pthread_create (&h->lp_thread,
    450                         NULL,
    451                         &TALER_FAKEBANK_lp_expiration_thread_,
    452                         h))
    453     {
    454       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    455                            "pthread_create");
    456 #ifdef __linux__
    457       GNUNET_break (0 == close (h->lp_event));
    458       h->lp_event = -1;
    459 #else
    460       GNUNET_break (0 == close (h->lp_event_in));
    461       GNUNET_break (0 == close (h->lp_event_out));
    462       h->lp_event_in = -1;
    463       h->lp_event_out = -1;
    464 #endif
    465       TALER_FAKEBANK_stop (h);
    466       return NULL;
    467     }
    468     h->mhd_bank = MHD_start_daemon (
    469       MHD_USE_DEBUG
    470       | MHD_USE_AUTO_INTERNAL_THREAD
    471       | MHD_ALLOW_SUSPEND_RESUME
    472       | MHD_USE_TURBO
    473       | MHD_USE_TCP_FASTOPEN
    474       | MHD_USE_DUAL_STACK,
    475       port,
    476       NULL, NULL,
    477       &handle_mhd_request, h,
    478       MHD_OPTION_NOTIFY_COMPLETED,
    479       &handle_mhd_completion_callback, h,
    480       MHD_OPTION_LISTEN_BACKLOG_SIZE,
    481       (unsigned int) 1024,
    482       MHD_OPTION_CONNECTION_LIMIT,
    483       (unsigned int) 65536,
    484       MHD_OPTION_THREAD_POOL_SIZE,
    485       num_threads,
    486       MHD_OPTION_END);
    487     if (NULL == h->mhd_bank)
    488     {
    489       GNUNET_break (0);
    490       TALER_FAKEBANK_stop (h);
    491       return NULL;
    492     }
    493   }
    494   return h;
    495 }
    496 
    497 
    498 /* end of fakebank.c */