/* This file is part of TALER (C) 2014--2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. TALER is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, see */ /** * @file taler-merchant-benchmark.c * @brief benchmark the backend to evaluate performance * @author Marcello Stanisci * @author Christian Grothoff */ #include "platform.h" #include #include #include "taler_merchant_util.h" #include "taler_merchant_testing_lib.h" /** * Maximum length of an amount (value plus currency string) needed by the test. * We have a 32-bit and a 64-bit value (~48 characters), plus the currency, plus * some punctuation. */ #define MAX_AMOUNT_LEN (TALER_CURRENCY_LEN + 64) /** * Maximum length of an order JSON. Generously allocated. */ #define MAX_ORDER_LEN (MAX_AMOUNT_LEN * 4 + 2048) /** * ID to use for the 'alternative' instance. */ static const char *alt_instance_id = "alt"; /** * What API key should we send in the HTTP 'Authorization' header? */ static char *apikey; /** * Witnesses if the ordinary cases payment suite should be run. */ static bool ordinary; /** * Witnesses if the corner cases payment suite should be run. */ static bool corner; /** * Base URL of the alternative non default instance. */ static char *alt_instance_url; /** * How many unaggregated payments we want to generate. */ static unsigned int unaggregated_number = 1; /** * How many payments that use two coins we want to generate. */ static unsigned int twocoins_number = 1; /** * How many payments we want to generate. */ static unsigned int payments_number = 1; /** * Config filename to give to commands (like wirewatch). */ static char *cfg_filename; /** * Merchant base URL. */ static char *merchant_url; /** * Currency used. */ static char *currency; /** * Set to 1 if `-f` command line option given. */ static int use_fakebank; /** * Configuration section with details about the exchange * bank account to use. */ static char *exchange_bank_section; /** * Credentials to use for the benchmark. */ static struct TALER_TESTING_Credentials cred; /** * Actual commands collection. */ static void run (void *cls, struct TALER_TESTING_Interpreter *is) { char CURRENCY_10_02[MAX_AMOUNT_LEN]; char CURRENCY_10[MAX_AMOUNT_LEN]; char CURRENCY_9_98[MAX_AMOUNT_LEN]; char CURRENCY_5_01[MAX_AMOUNT_LEN]; char CURRENCY_5[MAX_AMOUNT_LEN]; char CURRENCY_4_99[MAX_AMOUNT_LEN]; char CURRENCY_4_98[MAX_AMOUNT_LEN]; char CURRENCY_0_02[MAX_AMOUNT_LEN]; char CURRENCY_0_01[MAX_AMOUNT_LEN]; GNUNET_snprintf (CURRENCY_10_02, sizeof (CURRENCY_10_02), "%s:10.02", currency); GNUNET_snprintf (CURRENCY_10, sizeof (CURRENCY_10), "%s:10", currency); GNUNET_snprintf (CURRENCY_9_98, sizeof (CURRENCY_9_98), "%s:9.98", currency); GNUNET_snprintf (CURRENCY_5_01, sizeof (CURRENCY_5_01), "%s:5.01", currency); GNUNET_snprintf (CURRENCY_5, sizeof (CURRENCY_5), "%s:5", currency); GNUNET_snprintf (CURRENCY_4_99, sizeof (CURRENCY_4_99), "%s:4.99", currency); GNUNET_snprintf (CURRENCY_4_98, sizeof (CURRENCY_4_98), "%s:4.98", currency); GNUNET_snprintf (CURRENCY_0_02, sizeof (CURRENCY_0_02), "%s:0.02", currency); GNUNET_snprintf (CURRENCY_0_01, sizeof (CURRENCY_0_01), "%s:0.01", currency); if (ordinary) { struct TALER_TESTING_Command ordinary_commands[] = { TALER_TESTING_cmd_get_exchange ( "get-exchange", cred.cfg, NULL, true, true), TALER_TESTING_cmd_set_authorization ( "set-auth-valid", apikey), TALER_TESTING_cmd_merchant_post_instances ( "instance-create-default", merchant_url, "default", MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_account ( "instance-create-default-account", merchant_url, cred.user42_payto, NULL, NULL, MHD_HTTP_OK), TALER_TESTING_cmd_admin_add_incoming ( "create-reserve-1", CURRENCY_10_02, &cred.ba, cred.user43_payto), TALER_TESTING_cmd_exec_wirewatch2 ( "wirewatch-1", cfg_filename, exchange_bank_section), TALER_TESTING_cmd_withdraw_amount ( "withdraw-coin-1", "create-reserve-1", CURRENCY_5, 0, MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ( "withdraw-coin-2", "create-reserve-1", CURRENCY_5, 0, MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_orders ( "create-proposal-1", cred.cfg, merchant_url, MHD_HTTP_OK, NULL, /* random order ID please */ GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, CURRENCY_5), TALER_TESTING_cmd_merchant_pay_order ( "deposit-simple", merchant_url, MHD_HTTP_OK, "create-proposal-1", "withdraw-coin-1", CURRENCY_5, CURRENCY_4_99, NULL), TALER_TESTING_cmd_rewind_ip ( "rewind-payments", "create-reserve-1", payments_number), TALER_TESTING_cmd_exec_aggregator ( "aggregate-1x", cfg_filename), TALER_TESTING_cmd_exec_transfer ( "transfer-1", cfg_filename), TALER_TESTING_cmd_end () }; TALER_TESTING_run (is, ordinary_commands); return; } if (corner) /* should never be 'false' here */ { struct TALER_TESTING_Command corner_commands[] = { TALER_TESTING_cmd_get_exchange ( "get-exchange", cred.cfg, NULL, true, true), TALER_TESTING_cmd_set_authorization ( "set-auth-valid", apikey), TALER_TESTING_cmd_merchant_post_instances ( "instance-create-default", merchant_url, "default", MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_account ( "instance-create-default-account", merchant_url, cred.user42_payto, NULL, NULL, MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_instances ( "instance-create-alt", merchant_url, alt_instance_id, MHD_HTTP_NO_CONTENT), TALER_TESTING_cmd_merchant_post_account ( "instance-create-alt-account", alt_instance_url, cred.user42_payto, NULL, NULL, MHD_HTTP_OK), TALER_TESTING_cmd_admin_add_incoming ( "create-reserve-1", CURRENCY_5_01, &cred.ba, cred.user43_payto), TALER_TESTING_cmd_exec_wirewatch2 ( "wirewatch-1", cfg_filename, exchange_bank_section), TALER_TESTING_cmd_withdraw_amount ( "withdraw-coin-1", "create-reserve-1", CURRENCY_5, 0, MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_orders ( "create-unaggregated-proposal", cred.cfg, alt_instance_url, MHD_HTTP_OK, NULL, /* use random order ID */ GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, CURRENCY_5), TALER_TESTING_cmd_merchant_pay_order ( "deposit-unaggregated", alt_instance_url, MHD_HTTP_OK, "create-unaggregated-proposal", "withdraw-coin-1", CURRENCY_5, CURRENCY_4_99, NULL), TALER_TESTING_cmd_rewind_ip ( "rewind-unaggregated", "create-reserve-1", unaggregated_number), TALER_TESTING_cmd_admin_add_incoming ( "create-reserve-2", CURRENCY_10_02, &cred.ba, cred.user43_payto), TALER_TESTING_cmd_exec_wirewatch2 ( "wirewatch-2", cfg_filename, exchange_bank_section), TALER_TESTING_cmd_withdraw_amount ( "withdraw-coin-2", "create-reserve-2", CURRENCY_5, 0, MHD_HTTP_OK), TALER_TESTING_cmd_withdraw_amount ( "withdraw-coin-3", "create-reserve-2", CURRENCY_5, 0, MHD_HTTP_OK), TALER_TESTING_cmd_merchant_post_orders ( "create-twocoins-proposal", cred.cfg, merchant_url, MHD_HTTP_OK, NULL, /* use random order ID */ GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, CURRENCY_10), TALER_TESTING_cmd_merchant_pay_order ( "deposit-twocoins", merchant_url, MHD_HTTP_OK, "create-twocoins-proposal", "withdraw-coin-2;withdraw-coin-3", CURRENCY_10, CURRENCY_9_98, NULL), TALER_TESTING_cmd_exec_aggregator ( "aggregate-twocoins", cfg_filename), TALER_TESTING_cmd_exec_transfer ( "transfer-twocoins", cfg_filename), TALER_TESTING_cmd_rewind_ip ( "rewind-twocoins", "create-reserve-2", twocoins_number), TALER_TESTING_cmd_end () }; TALER_TESTING_run (is, corner_commands); return; } } /** * The main function of the serve tool * * @param argc number of arguments from the command line * @param argv command line arguments * @return 0 ok, or `enum PaymentGeneratorError` on error */ int main (int argc, char *const *argv) { char *loglev = NULL; char *logfile = NULL; char *exchange_account = NULL; struct GNUNET_GETOPT_CommandLineOption *options; struct GNUNET_GETOPT_CommandLineOption root_options[] = { GNUNET_GETOPT_option_cfgfile (&cfg_filename), GNUNET_GETOPT_option_string ( 'u', "exchange-account-section", "SECTION", "use exchange bank account configuration from the given SECTION", &exchange_bank_section), GNUNET_GETOPT_option_flag ( 'f', "fakebank", "use fakebank for the banking system", &use_fakebank), GNUNET_GETOPT_option_version (PACKAGE_VERSION " " VCS_VERSION), GNUNET_GETOPT_option_help ( TALER_MERCHANT_project_data (), "Runs benchmark logic against merchant backend. " "Must be used with either 'ordinary' or 'corner' sub-commands."), GNUNET_GETOPT_option_string ( 'l', "logfile", "LF", "will log to file LF", &logfile), GNUNET_GETOPT_option_loglevel (&loglev), GNUNET_GETOPT_OPTION_END }; struct GNUNET_GETOPT_CommandLineOption corner_options[] = { GNUNET_GETOPT_option_string ( 'a', "apikey", "APIKEY", "HTTP 'Authorization' header to send to the merchant", &apikey), GNUNET_GETOPT_option_cfgfile (&cfg_filename), GNUNET_GETOPT_option_flag ( 'f', "fakebank", "use fakebank for the banking system", &use_fakebank), GNUNET_GETOPT_option_help ( TALER_MERCHANT_project_data (), "Populate databases with corner case payments"), GNUNET_GETOPT_option_string ( 'l', "logfile", "LF", "will log to file LF", &logfile), GNUNET_GETOPT_option_loglevel (&loglev), GNUNET_GETOPT_option_uint ( 't', "two-coins", "TC", "will perform TC 2-coins payments, defaults to 1", &twocoins_number), GNUNET_GETOPT_option_uint ( 'U', "unaggregated-number", "UN", "will generate UN unaggregated payments, defaults to 1", &unaggregated_number), GNUNET_GETOPT_option_string ( 'u', "exchange-account-section", "SECTION", "use exchange bank account configuration from the given SECTION", &exchange_bank_section), GNUNET_GETOPT_OPTION_END }; struct GNUNET_GETOPT_CommandLineOption ordinary_options[] = { GNUNET_GETOPT_option_string ( 'a', "apikey", "APIKEY", "HTTP 'Authorization' header to send to the merchant", &apikey), GNUNET_GETOPT_option_cfgfile (&cfg_filename), GNUNET_GETOPT_option_mandatory ( GNUNET_GETOPT_option_string ( 'e', "exchange-account", "SECTION", "configuration section specifying the exchange account to use, mandatory", &exchange_account)), GNUNET_GETOPT_option_flag ( 'f', "fakebank", "use fakebank for the banking system", &use_fakebank), GNUNET_GETOPT_option_help ( TALER_MERCHANT_project_data (), "Generate Taler ordinary payments" " to populate the databases"), GNUNET_GETOPT_option_string ( 'l', "logfile", "LF", "will log to file LF", &logfile), GNUNET_GETOPT_option_loglevel (&loglev), GNUNET_GETOPT_option_uint ( 'p', "payments-number", "PN", "will generate PN payments, defaults to 1", &payments_number), GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION), GNUNET_GETOPT_OPTION_END }; const char *default_config_file; default_config_file = TALER_MERCHANT_project_data ()->user_config_file; options = root_options; if (NULL != argv[1]) { if (0 == strcmp ("ordinary", argv[1])) { ordinary = true; options = ordinary_options; } if (0 == strcmp ("corner", argv[1])) { corner = true; options = corner_options; } } { enum GNUNET_GenericReturnValue result; result = GNUNET_GETOPT_run ("taler-merchant-benchmark", options, argc, argv); switch (result) { case GNUNET_SYSERR: return EXIT_INVALIDARGUMENT; case GNUNET_NO: return EXIT_SUCCESS; case GNUNET_OK: break; } } if (NULL == exchange_bank_section) exchange_bank_section = "exchange-account-1"; if (NULL == loglev) loglev = "INFO"; GNUNET_log_setup ("taler-merchant-benchmark", loglev, logfile); if ( (! ordinary) && (! corner) ) { TALER_LOG_ERROR ("Please use 'ordinary' or 'corner' subcommands.\n"); return EXIT_INVALIDARGUMENT; } if (NULL == cfg_filename) cfg_filename = (char *) default_config_file; /* load currency from configuration */ { struct GNUNET_CONFIGURATION_Handle *cfg; cfg = GNUNET_CONFIGURATION_create (TALER_MERCHANT_project_data ()); if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_filename)) { TALER_LOG_ERROR ("Could not parse configuration\n"); return EXIT_NOTCONFIGURED; } if (GNUNET_OK != TALER_config_get_currency (cfg, ¤cy)) { TALER_LOG_ERROR ("Failed to read currency from configuration\n"); GNUNET_CONFIGURATION_destroy (cfg); return EXIT_NOTCONFIGURED; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "merchant-benchmark", "MERCHANT_URL", &merchant_url)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "merchant-benchmark", "MERCHANT_URL"); GNUNET_CONFIGURATION_destroy (cfg); return EXIT_NOTCONFIGURED; } if ( (0 == strlen (merchant_url)) || (merchant_url[strlen (merchant_url) - 1] != '/') ) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "merchant-benchmark", "MERCHANT_URL", "Not a valid URL"); GNUNET_CONFIGURATION_destroy (cfg); return EXIT_NOTCONFIGURED; } if (GNUNET_OK != TALER_TESTING_get_credentials ( cfg_filename, exchange_bank_section, use_fakebank ? TALER_TESTING_BS_FAKEBANK : TALER_TESTING_BS_IBAN, &cred)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Required bank credentials not given in configuration\n"); GNUNET_free (cfg_filename); return EXIT_NOTCONFIGURED; } GNUNET_CONFIGURATION_destroy (cfg); } GNUNET_asprintf (&alt_instance_url, "%sinstances/%s/", merchant_url, alt_instance_id); { enum GNUNET_GenericReturnValue result; result = TALER_TESTING_loop (&run, NULL); return (GNUNET_OK == result) ? 0 : EXIT_FAILURE; } }