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 ¤cy)) 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 }