merchant

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

taler-merchant-benchmark.c (17522B)


      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 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_merchant_util.h"
     29 #include "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   char *exchange_account = NULL;
    395   struct GNUNET_GETOPT_CommandLineOption *options;
    396   struct GNUNET_GETOPT_CommandLineOption root_options[] = {
    397     GNUNET_GETOPT_option_cfgfile (&cfg_filename),
    398     GNUNET_GETOPT_option_string (
    399       'u',
    400       "exchange-account-section",
    401       "SECTION",
    402       "use exchange bank account configuration from the given SECTION",
    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 " " VCS_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       &exchange_bank_section),
    464     GNUNET_GETOPT_OPTION_END
    465   };
    466   struct GNUNET_GETOPT_CommandLineOption ordinary_options[] = {
    467     GNUNET_GETOPT_option_string (
    468       'a',
    469       "apikey",
    470       "APIKEY",
    471       "HTTP 'Authorization' header to send to the merchant",
    472       &apikey),
    473     GNUNET_GETOPT_option_cfgfile (&cfg_filename),
    474     GNUNET_GETOPT_option_mandatory (
    475       GNUNET_GETOPT_option_string (
    476         'e',
    477         "exchange-account",
    478         "SECTION",
    479         "configuration section specifying the exchange account to use, mandatory",
    480         &exchange_account)),
    481     GNUNET_GETOPT_option_flag (
    482       'f',
    483       "fakebank",
    484       "use fakebank for the banking system",
    485       &use_fakebank),
    486     GNUNET_GETOPT_option_help (
    487       TALER_MERCHANT_project_data (),
    488       "Generate Taler ordinary payments"
    489       " to populate the databases"),
    490     GNUNET_GETOPT_option_string (
    491       'l',
    492       "logfile",
    493       "LF",
    494       "will log to file LF",
    495       &logfile),
    496     GNUNET_GETOPT_option_loglevel (&loglev),
    497     GNUNET_GETOPT_option_uint (
    498       'p',
    499       "payments-number",
    500       "PN",
    501       "will generate PN payments, defaults to 1",
    502       &payments_number),
    503     GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
    504     GNUNET_GETOPT_OPTION_END
    505   };
    506   const char *default_config_file;
    507 
    508   default_config_file = TALER_MERCHANT_project_data ()->user_config_file;
    509   options = root_options;
    510   if (NULL != argv[1])
    511   {
    512     if (0 == strcmp ("ordinary", argv[1]))
    513     {
    514       ordinary = true;
    515       options = ordinary_options;
    516     }
    517     if (0 == strcmp ("corner", argv[1]))
    518     {
    519       corner = true;
    520       options = corner_options;
    521     }
    522   }
    523 
    524   {
    525     enum GNUNET_GenericReturnValue result;
    526 
    527     result = GNUNET_GETOPT_run ("taler-merchant-benchmark",
    528                                 options,
    529                                 argc,
    530                                 argv);
    531     switch (result)
    532     {
    533     case GNUNET_SYSERR:
    534       return EXIT_INVALIDARGUMENT;
    535     case GNUNET_NO:
    536       return EXIT_SUCCESS;
    537     case GNUNET_OK:
    538       break;
    539     }
    540   }
    541   if (NULL == exchange_bank_section)
    542     exchange_bank_section = GNUNET_strdup ("exchange-account-1");
    543   if (NULL == loglev)
    544     loglev = GNUNET_strdup ("INFO");
    545   GNUNET_log_setup ("taler-merchant-benchmark",
    546                     loglev,
    547                     logfile);
    548   GNUNET_free (loglev);
    549   if ( (! ordinary) &&
    550        (! corner) )
    551   {
    552     TALER_LOG_ERROR ("Please use 'ordinary' or 'corner' subcommands.\n");
    553     return EXIT_INVALIDARGUMENT;
    554   }
    555   if (NULL == cfg_filename)
    556     cfg_filename = (char *) default_config_file;
    557   /* load currency from configuration */
    558   {
    559     struct GNUNET_CONFIGURATION_Handle *cfg;
    560 
    561     cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ());
    562     if (GNUNET_OK !=
    563         GNUNET_CONFIGURATION_load (cfg,
    564                                    cfg_filename))
    565     {
    566       TALER_LOG_ERROR ("Could not parse configuration\n");
    567       return EXIT_NOTCONFIGURED;
    568     }
    569     if (GNUNET_OK !=
    570         TALER_config_get_currency (cfg,
    571                                    "merchant",
    572                                    &currency))
    573     {
    574       TALER_LOG_ERROR ("Failed to read currency from configuration\n");
    575       GNUNET_CONFIGURATION_destroy (cfg);
    576       return EXIT_NOTCONFIGURED;
    577     }
    578     if (GNUNET_OK !=
    579         GNUNET_CONFIGURATION_get_value_string (cfg,
    580                                                "merchant-benchmark",
    581                                                "MERCHANT_URL",
    582                                                &merchant_url))
    583     {
    584       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    585                                  "merchant-benchmark",
    586                                  "MERCHANT_URL");
    587       GNUNET_CONFIGURATION_destroy (cfg);
    588       return EXIT_NOTCONFIGURED;
    589     }
    590     if ( (0 == strlen (merchant_url)) ||
    591          (merchant_url[strlen (merchant_url) - 1] != '/') )
    592     {
    593       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    594                                  "merchant-benchmark",
    595                                  "MERCHANT_URL",
    596                                  "Not a valid URL");
    597       GNUNET_CONFIGURATION_destroy (cfg);
    598       return EXIT_NOTCONFIGURED;
    599     }
    600 
    601     if (GNUNET_OK !=
    602         TALER_TESTING_get_credentials (
    603           cfg_filename,
    604           exchange_bank_section,
    605           use_fakebank
    606           ? TALER_TESTING_BS_FAKEBANK
    607           : TALER_TESTING_BS_IBAN,
    608           &cred))
    609     {
    610       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    611                   "Required bank credentials not given in configuration\n");
    612       GNUNET_free (cfg_filename);
    613       return EXIT_NOTCONFIGURED;
    614     }
    615 
    616     GNUNET_CONFIGURATION_destroy (cfg);
    617   }
    618   GNUNET_asprintf (&alt_instance_url,
    619                    "%sinstances/%s/",
    620                    merchant_url,
    621                    alt_instance_id);
    622   {
    623     enum GNUNET_GenericReturnValue result;
    624 
    625     result = TALER_TESTING_loop (&run,
    626                                  NULL);
    627     return (GNUNET_OK == result)
    628       ? 0
    629       : EXIT_FAILURE;
    630   }
    631 }