merchant

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

taler-merchant-benchmark.c (17299B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014--2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it
      6   under the terms of the GNU Affero General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13   General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public License
     16   along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file src/merchant-tools/taler-merchant-benchmark.c
     21  * @brief benchmark the backend to evaluate performance
     22  * @author Marcello Stanisci
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include <taler/taler_util.h>
     27 #include <taler/taler_testing_lib.h>
     28 #include "taler/taler_merchant_util.h"
     29 #include "taler/taler_merchant_testing_lib.h"
     30 
     31 
     32 /**
     33  * Maximum length of an amount (value plus currency string) needed by the test.
     34  * We have a 32-bit and a 64-bit value (~48 characters), plus the currency, plus
     35  * some punctuation.
     36  */
     37 #define MAX_AMOUNT_LEN (TALER_CURRENCY_LEN + 64)
     38 
     39 /**
     40  * Maximum length of an order JSON.  Generously allocated.
     41  */
     42 #define MAX_ORDER_LEN (MAX_AMOUNT_LEN * 4 + 2048)
     43 
     44 
     45 /**
     46  * ID to use for the 'alternative' instance.
     47  */
     48 static const char *alt_instance_id = "alt";
     49 
     50 /**
     51  * What API key should we send in the HTTP 'Authorization' header?
     52  */
     53 static char *apikey;
     54 
     55 /**
     56  * Witnesses if the ordinary cases payment suite should be run.
     57  */
     58 static bool ordinary;
     59 
     60 /**
     61  * Witnesses if the corner cases payment suite should be run.
     62  */
     63 static bool corner;
     64 
     65 /**
     66  * Base URL of the alternative non default instance.
     67  */
     68 static char *alt_instance_url;
     69 
     70 /**
     71  * How many unaggregated payments we want to generate.
     72  */
     73 static unsigned int unaggregated_number = 1;
     74 
     75 /**
     76  * How many payments that use two coins we want to generate.
     77  */
     78 static unsigned int twocoins_number = 1;
     79 
     80 /**
     81  * How many payments we want to generate.
     82  */
     83 static unsigned int payments_number = 1;
     84 
     85 /**
     86  * Config filename to give to commands (like wirewatch).
     87  */
     88 static char *cfg_filename;
     89 
     90 /**
     91  * Merchant base URL.
     92  */
     93 static char *merchant_url;
     94 
     95 /**
     96  * Currency used.
     97  */
     98 static char *currency;
     99 
    100 /**
    101  * Set to 1 if `-f` command line option given.
    102  */
    103 static int use_fakebank;
    104 
    105 /**
    106  * Configuration section with details about the exchange
    107  * bank account to use.
    108  */
    109 static char *exchange_bank_section;
    110 
    111 /**
    112  * Credentials to use for the benchmark.
    113  */
    114 static struct TALER_TESTING_Credentials cred;
    115 
    116 
    117 /**
    118  * Actual commands collection.
    119  */
    120 static void
    121 run (void *cls,
    122      struct TALER_TESTING_Interpreter *is)
    123 {
    124   char CURRENCY_10_02[MAX_AMOUNT_LEN];
    125   char CURRENCY_10[MAX_AMOUNT_LEN];
    126   char CURRENCY_9_98[MAX_AMOUNT_LEN];
    127   char CURRENCY_5_01[MAX_AMOUNT_LEN];
    128   char CURRENCY_5[MAX_AMOUNT_LEN];
    129   char CURRENCY_4_99[MAX_AMOUNT_LEN];
    130   char CURRENCY_4_98[MAX_AMOUNT_LEN];
    131   char CURRENCY_0_02[MAX_AMOUNT_LEN];
    132   char CURRENCY_0_01[MAX_AMOUNT_LEN];
    133 
    134   GNUNET_snprintf (CURRENCY_10_02,
    135                    sizeof (CURRENCY_10_02),
    136                    "%s:10.02",
    137                    currency);
    138   GNUNET_snprintf (CURRENCY_10,
    139                    sizeof (CURRENCY_10),
    140                    "%s:10",
    141                    currency);
    142   GNUNET_snprintf (CURRENCY_9_98,
    143                    sizeof (CURRENCY_9_98),
    144                    "%s:9.98",
    145                    currency);
    146   GNUNET_snprintf (CURRENCY_5_01,
    147                    sizeof (CURRENCY_5_01),
    148                    "%s:5.01",
    149                    currency);
    150   GNUNET_snprintf (CURRENCY_5,
    151                    sizeof (CURRENCY_5),
    152                    "%s:5",
    153                    currency);
    154   GNUNET_snprintf (CURRENCY_4_99,
    155                    sizeof (CURRENCY_4_99),
    156                    "%s:4.99",
    157                    currency);
    158   GNUNET_snprintf (CURRENCY_4_98,
    159                    sizeof (CURRENCY_4_98),
    160                    "%s:4.98",
    161                    currency);
    162   GNUNET_snprintf (CURRENCY_0_02,
    163                    sizeof (CURRENCY_0_02),
    164                    "%s:0.02",
    165                    currency);
    166   GNUNET_snprintf (CURRENCY_0_01,
    167                    sizeof (CURRENCY_0_01),
    168                    "%s:0.01",
    169                    currency);
    170   if (ordinary)
    171   {
    172     struct TALER_TESTING_Command ordinary_commands[] = {
    173       TALER_TESTING_cmd_get_exchange (
    174         "get-exchange",
    175         cred.cfg,
    176         NULL,
    177         true,
    178         true),
    179       TALER_TESTING_cmd_set_authorization (
    180         "set-auth-valid",
    181         apikey),
    182       TALER_TESTING_cmd_merchant_post_instances (
    183         "instance-create-admin",
    184         merchant_url,
    185         "admin",
    186         MHD_HTTP_NO_CONTENT),
    187       TALER_TESTING_cmd_merchant_post_account (
    188         "instance-create-default-account",
    189         merchant_url,
    190         cred.user42_payto,
    191         NULL, NULL,
    192         MHD_HTTP_OK),
    193       TALER_TESTING_cmd_admin_add_incoming (
    194         "create-reserve-1",
    195         CURRENCY_10_02,
    196         &cred.ba,
    197         cred.user43_payto),
    198       TALER_TESTING_cmd_exec_wirewatch2 (
    199         "wirewatch-1",
    200         cfg_filename,
    201         exchange_bank_section),
    202       TALER_TESTING_cmd_withdraw_amount (
    203         "withdraw-coin-1",
    204         "create-reserve-1",
    205         CURRENCY_5,
    206         0,
    207         MHD_HTTP_OK),
    208       TALER_TESTING_cmd_withdraw_amount (
    209         "withdraw-coin-2",
    210         "create-reserve-1",
    211         CURRENCY_5,
    212         0,
    213         MHD_HTTP_OK),
    214       TALER_TESTING_cmd_merchant_post_orders (
    215         "create-proposal-1",
    216         cred.cfg,
    217         merchant_url,
    218         MHD_HTTP_OK,
    219         NULL,  /* random order ID please */
    220         GNUNET_TIME_UNIT_ZERO_TS,
    221         GNUNET_TIME_UNIT_FOREVER_TS,
    222         CURRENCY_5),
    223       TALER_TESTING_cmd_merchant_pay_order (
    224         "deposit-simple",
    225         merchant_url,
    226         MHD_HTTP_OK,
    227         "create-proposal-1",
    228         "withdraw-coin-1",
    229         CURRENCY_5,
    230         CURRENCY_4_99,
    231         NULL),
    232       TALER_TESTING_cmd_rewind_ip (
    233         "rewind-payments",
    234         "create-reserve-1",
    235         payments_number),
    236       TALER_TESTING_cmd_exec_aggregator (
    237         "aggregate-1x",
    238         cfg_filename),
    239       TALER_TESTING_cmd_exec_transfer (
    240         "transfer-1",
    241         cfg_filename),
    242 
    243       TALER_TESTING_cmd_end ()
    244     };
    245 
    246     TALER_TESTING_run (is,
    247                        ordinary_commands);
    248     return;
    249   }
    250 
    251   if (corner) /* should never be 'false' here */
    252   {
    253     struct TALER_TESTING_Command corner_commands[] = {
    254       TALER_TESTING_cmd_get_exchange (
    255         "get-exchange",
    256         cred.cfg,
    257         NULL,
    258         true,
    259         true),
    260       TALER_TESTING_cmd_set_authorization (
    261         "set-auth-valid",
    262         apikey),
    263       TALER_TESTING_cmd_merchant_post_instances (
    264         "instance-create-admin",
    265         merchant_url,
    266         "admin",
    267         MHD_HTTP_NO_CONTENT),
    268       TALER_TESTING_cmd_merchant_post_account (
    269         "instance-create-default-account",
    270         merchant_url,
    271         cred.user42_payto,
    272         NULL, NULL,
    273         MHD_HTTP_OK),
    274       TALER_TESTING_cmd_merchant_post_instances (
    275         "instance-create-alt",
    276         merchant_url,
    277         alt_instance_id,
    278         MHD_HTTP_NO_CONTENT),
    279       TALER_TESTING_cmd_merchant_post_account (
    280         "instance-create-alt-account",
    281         alt_instance_url,
    282         cred.user42_payto,
    283         NULL, NULL,
    284         MHD_HTTP_OK),
    285       TALER_TESTING_cmd_admin_add_incoming (
    286         "create-reserve-1",
    287         CURRENCY_5_01,
    288         &cred.ba,
    289         cred.user43_payto),
    290       TALER_TESTING_cmd_exec_wirewatch2 (
    291         "wirewatch-1",
    292         cfg_filename,
    293         exchange_bank_section),
    294       TALER_TESTING_cmd_withdraw_amount (
    295         "withdraw-coin-1",
    296         "create-reserve-1",
    297         CURRENCY_5,
    298         0,
    299         MHD_HTTP_OK),
    300       TALER_TESTING_cmd_merchant_post_orders (
    301         "create-unaggregated-proposal",
    302         cred.cfg,
    303         alt_instance_url,
    304         MHD_HTTP_OK,
    305         NULL,  /* use random order ID */
    306         GNUNET_TIME_UNIT_ZERO_TS,
    307         GNUNET_TIME_UNIT_FOREVER_TS,
    308         CURRENCY_5),
    309       TALER_TESTING_cmd_merchant_pay_order (
    310         "deposit-unaggregated",
    311         alt_instance_url,
    312         MHD_HTTP_OK,
    313         "create-unaggregated-proposal",
    314         "withdraw-coin-1",
    315         CURRENCY_5,
    316         CURRENCY_4_99,
    317         NULL),
    318       TALER_TESTING_cmd_rewind_ip (
    319         "rewind-unaggregated",
    320         "create-reserve-1",
    321         unaggregated_number),
    322       TALER_TESTING_cmd_admin_add_incoming (
    323         "create-reserve-2",
    324         CURRENCY_10_02,
    325         &cred.ba,
    326         cred.user43_payto),
    327       TALER_TESTING_cmd_exec_wirewatch2 (
    328         "wirewatch-2",
    329         cfg_filename,
    330         exchange_bank_section),
    331       TALER_TESTING_cmd_withdraw_amount (
    332         "withdraw-coin-2",
    333         "create-reserve-2",
    334         CURRENCY_5,
    335         0,
    336         MHD_HTTP_OK),
    337       TALER_TESTING_cmd_withdraw_amount (
    338         "withdraw-coin-3",
    339         "create-reserve-2",
    340         CURRENCY_5,
    341         0,
    342         MHD_HTTP_OK),
    343       TALER_TESTING_cmd_merchant_post_orders (
    344         "create-twocoins-proposal",
    345         cred.cfg,
    346         merchant_url,
    347         MHD_HTTP_OK,
    348         NULL, /* use random order ID */
    349         GNUNET_TIME_UNIT_ZERO_TS,
    350         GNUNET_TIME_UNIT_FOREVER_TS,
    351         CURRENCY_10),
    352       TALER_TESTING_cmd_merchant_pay_order (
    353         "deposit-twocoins",
    354         merchant_url,
    355         MHD_HTTP_OK,
    356         "create-twocoins-proposal",
    357         "withdraw-coin-2;withdraw-coin-3",
    358         CURRENCY_10,
    359         CURRENCY_9_98,
    360         NULL),
    361       TALER_TESTING_cmd_exec_aggregator (
    362         "aggregate-twocoins",
    363         cfg_filename),
    364       TALER_TESTING_cmd_exec_transfer (
    365         "transfer-twocoins",
    366         cfg_filename),
    367       TALER_TESTING_cmd_rewind_ip (
    368         "rewind-twocoins",
    369         "create-reserve-2",
    370         twocoins_number),
    371       TALER_TESTING_cmd_end ()
    372     };
    373 
    374     TALER_TESTING_run (is,
    375                        corner_commands);
    376     return;
    377   }
    378 }
    379 
    380 
    381 /**
    382  * The main function of the serve tool
    383  *
    384  * @param argc number of arguments from the command line
    385  * @param argv command line arguments
    386  * @return 0 ok, or `enum PaymentGeneratorError` on error
    387  */
    388 int
    389 main (int argc,
    390       char *const *argv)
    391 {
    392   char *loglev = NULL;
    393   char *logfile = NULL;
    394   struct GNUNET_GETOPT_CommandLineOption *options;
    395   struct GNUNET_GETOPT_CommandLineOption root_options[] = {
    396     GNUNET_GETOPT_option_cfgfile (&cfg_filename),
    397     GNUNET_GETOPT_option_string (
    398       'u',
    399       "exchange-account-section",
    400       "SECTION",
    401       "use exchange bank account configuration from the given SECTION,"
    402       " defaults to \"exchange-account-1\"",
    403       &exchange_bank_section),
    404     GNUNET_GETOPT_option_flag (
    405       'f',
    406       "fakebank",
    407       "use fakebank for the banking system",
    408       &use_fakebank),
    409     GNUNET_GETOPT_option_version (PACKAGE_VERSION),
    410     GNUNET_GETOPT_option_help (
    411       TALER_MERCHANT_project_data (),
    412       "Runs benchmark logic against merchant backend. "
    413       "Must be used with either 'ordinary' or 'corner' sub-commands."),
    414     GNUNET_GETOPT_option_string (
    415       'l',
    416       "logfile",
    417       "LF",
    418       "will log to file LF",
    419       &logfile),
    420     GNUNET_GETOPT_option_loglevel (&loglev),
    421     GNUNET_GETOPT_OPTION_END
    422   };
    423   struct GNUNET_GETOPT_CommandLineOption corner_options[] = {
    424     GNUNET_GETOPT_option_string (
    425       'a',
    426       "apikey",
    427       "APIKEY",
    428       "HTTP 'Authorization' header to send to the merchant",
    429       &apikey),
    430     GNUNET_GETOPT_option_cfgfile (&cfg_filename),
    431     GNUNET_GETOPT_option_flag (
    432       'f',
    433       "fakebank",
    434       "use fakebank for the banking system",
    435       &use_fakebank),
    436     GNUNET_GETOPT_option_help (
    437       TALER_MERCHANT_project_data (),
    438       "Populate databases with corner case payments"),
    439     GNUNET_GETOPT_option_string (
    440       'l',
    441       "logfile",
    442       "LF",
    443       "will log to file LF",
    444       &logfile),
    445     GNUNET_GETOPT_option_loglevel (&loglev),
    446     GNUNET_GETOPT_option_uint (
    447       't',
    448       "two-coins",
    449       "TC",
    450       "will perform TC 2-coins payments, defaults to 1",
    451       &twocoins_number),
    452     GNUNET_GETOPT_option_uint (
    453       'U',
    454       "unaggregated-number",
    455       "UN",
    456       "will generate UN unaggregated payments, defaults to 1",
    457       &unaggregated_number),
    458     GNUNET_GETOPT_option_string (
    459       'u',
    460       "exchange-account-section",
    461       "SECTION",
    462       "use exchange bank account configuration from the given SECTION,"
    463       " defaults to \"exchange-account-1\"",
    464       &exchange_bank_section),
    465     GNUNET_GETOPT_OPTION_END
    466   };
    467   struct GNUNET_GETOPT_CommandLineOption ordinary_options[] = {
    468     GNUNET_GETOPT_option_string (
    469       'a',
    470       "apikey",
    471       "APIKEY",
    472       "HTTP 'Authorization' header to send to the merchant",
    473       &apikey),
    474     GNUNET_GETOPT_option_cfgfile (&cfg_filename),
    475     GNUNET_GETOPT_option_flag (
    476       'f',
    477       "fakebank",
    478       "use fakebank for the banking system",
    479       &use_fakebank),
    480     GNUNET_GETOPT_option_help (
    481       TALER_MERCHANT_project_data (),
    482       "Generate Taler ordinary payments"
    483       " to populate the databases"),
    484     GNUNET_GETOPT_option_string (
    485       'l',
    486       "logfile",
    487       "LF",
    488       "will log to file LF",
    489       &logfile),
    490     GNUNET_GETOPT_option_loglevel (&loglev),
    491     GNUNET_GETOPT_option_uint (
    492       'p',
    493       "payments-number",
    494       "PN",
    495       "will generate PN payments, defaults to 1",
    496       &payments_number),
    497     GNUNET_GETOPT_option_version (PACKAGE_VERSION),
    498     GNUNET_GETOPT_OPTION_END
    499   };
    500   const char *default_config_file;
    501 
    502   default_config_file = TALER_MERCHANT_project_data ()->user_config_file;
    503   options = root_options;
    504   if (NULL != argv[1])
    505   {
    506     if (0 == strcmp ("ordinary", argv[1]))
    507     {
    508       ordinary = true;
    509       options = ordinary_options;
    510     }
    511     if (0 == strcmp ("corner", argv[1]))
    512     {
    513       corner = true;
    514       options = corner_options;
    515     }
    516   }
    517 
    518   {
    519     enum GNUNET_GenericReturnValue result;
    520 
    521     result = GNUNET_GETOPT_run ("taler-merchant-benchmark",
    522                                 options,
    523                                 argc,
    524                                 argv);
    525     switch (result)
    526     {
    527     case GNUNET_SYSERR:
    528       return EXIT_INVALIDARGUMENT;
    529     case GNUNET_NO:
    530       return EXIT_SUCCESS;
    531     case GNUNET_OK:
    532       break;
    533     }
    534   }
    535   if (NULL == exchange_bank_section)
    536     exchange_bank_section = GNUNET_strdup ("exchange-account-1");
    537   if (NULL == loglev)
    538     loglev = GNUNET_strdup ("INFO");
    539   GNUNET_log_setup ("taler-merchant-benchmark",
    540                     loglev,
    541                     logfile);
    542   GNUNET_free (loglev);
    543   if ( (! ordinary) &&
    544        (! corner) )
    545   {
    546     TALER_LOG_ERROR ("Please use 'ordinary' or 'corner' subcommands.\n");
    547     return EXIT_INVALIDARGUMENT;
    548   }
    549   if (NULL == cfg_filename)
    550     cfg_filename = (char *) default_config_file;
    551   /* load currency from configuration */
    552   {
    553     struct GNUNET_CONFIGURATION_Handle *cfg;
    554 
    555     cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ());
    556     if (GNUNET_OK !=
    557         GNUNET_CONFIGURATION_load (cfg,
    558                                    cfg_filename))
    559     {
    560       TALER_LOG_ERROR ("Could not parse configuration\n");
    561       return EXIT_NOTCONFIGURED;
    562     }
    563     if (GNUNET_OK !=
    564         TALER_config_get_currency (cfg,
    565                                    "merchant",
    566                                    &currency))
    567     {
    568       TALER_LOG_ERROR ("Failed to read currency from configuration\n");
    569       GNUNET_CONFIGURATION_destroy (cfg);
    570       return EXIT_NOTCONFIGURED;
    571     }
    572     if (GNUNET_OK !=
    573         GNUNET_CONFIGURATION_get_value_string (cfg,
    574                                                "merchant-benchmark",
    575                                                "MERCHANT_URL",
    576                                                &merchant_url))
    577     {
    578       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    579                                  "merchant-benchmark",
    580                                  "MERCHANT_URL");
    581       GNUNET_CONFIGURATION_destroy (cfg);
    582       return EXIT_NOTCONFIGURED;
    583     }
    584     if ( (0 == strlen (merchant_url)) ||
    585          (merchant_url[strlen (merchant_url) - 1] != '/') )
    586     {
    587       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    588                                  "merchant-benchmark",
    589                                  "MERCHANT_URL",
    590                                  "Not a valid URL");
    591       GNUNET_CONFIGURATION_destroy (cfg);
    592       return EXIT_NOTCONFIGURED;
    593     }
    594 
    595     if (GNUNET_OK !=
    596         TALER_TESTING_get_credentials (
    597           cfg_filename,
    598           exchange_bank_section,
    599           use_fakebank
    600           ? TALER_TESTING_BS_FAKEBANK
    601           : TALER_TESTING_BS_IBAN,
    602           &cred))
    603     {
    604       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    605                   "Required bank credentials not given in configuration\n");
    606       return EXIT_NOTCONFIGURED;
    607     }
    608 
    609     GNUNET_CONFIGURATION_destroy (cfg);
    610   }
    611   GNUNET_asprintf (&alt_instance_url,
    612                    "%sinstances/%s/",
    613                    merchant_url,
    614                    alt_instance_id);
    615   {
    616     enum GNUNET_GenericReturnValue result;
    617 
    618     result = TALER_TESTING_loop (&run,
    619                                  NULL);
    620     return (GNUNET_OK == result)
    621       ? 0
    622       : EXIT_FAILURE;
    623   }
    624 }