test_exchange_p2p.c (18205B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014--2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU 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 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, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing/test_exchange_p2p.c 21 * @brief testcase to test exchange's P2P payments 22 * @author Christian Grothoff 23 */ 24 #include "taler/taler_util.h" 25 #include "taler/taler_json_lib.h" 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_testing_lib.h> 28 #include <microhttpd.h> 29 #include "taler/taler_bank_service.h" 30 #include "taler/taler_testing_lib.h" 31 32 /** 33 * Configuration file we use. One (big) configuration is used 34 * for the various components for this test. 35 */ 36 static char *config_file; 37 38 /** 39 * Our credentials. 40 */ 41 struct TALER_TESTING_Credentials cred; 42 43 /** 44 * Some tests behave differently when using CS as we cannot 45 * reuse the coin private key for different denominations 46 * due to the derivation of it with the /csr values. Hence 47 * some tests behave differently in CS mode, hence this 48 * flag. 49 */ 50 static bool uses_cs; 51 52 /** 53 * Execute the taler-exchange-wirewatch command with 54 * our configuration file. 55 * 56 * @param label label to use for the command. 57 */ 58 #define CMD_EXEC_WIREWATCH(label) \ 59 TALER_TESTING_cmd_exec_wirewatch2 (label, config_file, \ 60 "exchange-account-2") 61 62 /** 63 * Execute the taler-exchange-aggregator, closer and transfer commands with 64 * our configuration file. 65 * 66 * @param label label to use for the command. 67 */ 68 #define CMD_EXEC_AGGREGATOR(label) \ 69 TALER_TESTING_cmd_sleep ("sleep-before-aggregator", 2), \ 70 TALER_TESTING_cmd_exec_aggregator (label "-aggregator", config_file), \ 71 TALER_TESTING_cmd_exec_transfer (label "-transfer", config_file) 72 73 74 /** 75 * Run wire transfer of funds from some user's account to the 76 * exchange. 77 * 78 * @param label label to use for the command. 79 * @param amount amount to transfer, i.e. "EUR:1" 80 */ 81 #define CMD_TRANSFER_TO_EXCHANGE(label,amount) \ 82 TALER_TESTING_cmd_admin_add_incoming (label, amount, \ 83 &cred.ba, \ 84 cred.user42_payto) 85 86 /** 87 * Main function that will tell the interpreter what commands to 88 * run. 89 * 90 * @param cls closure 91 * @param is interpreter we use to run commands 92 */ 93 static void 94 run (void *cls, 95 struct TALER_TESTING_Interpreter *is) 96 { 97 /** 98 * Test withdrawal plus spending. 99 */ 100 struct TALER_TESTING_Command withdraw[] = { 101 /** 102 * Move money to the exchange's bank account. 103 */ 104 CMD_TRANSFER_TO_EXCHANGE ( 105 "create-reserve-1", 106 "EUR:5.04"), 107 CMD_TRANSFER_TO_EXCHANGE ( 108 "create-reserve-2", 109 "EUR:5.01"), 110 TALER_TESTING_cmd_reserve_poll ( 111 "poll-reserve-1", 112 "create-reserve-1", 113 "EUR:5.04", 114 GNUNET_TIME_UNIT_MINUTES, 115 MHD_HTTP_OK), 116 TALER_TESTING_cmd_check_bank_admin_transfer ( 117 "check-create-reserve-1", 118 "EUR:5.04", 119 cred.user42_payto, 120 cred.exchange_payto, 121 "create-reserve-1"), 122 TALER_TESTING_cmd_check_bank_admin_transfer ( 123 "check-create-reserve-2", 124 "EUR:5.01", 125 cred.user42_payto, 126 cred.exchange_payto, 127 "create-reserve-2"), 128 /** 129 * Make a reserve exist, according to the previous 130 * transfer. 131 */ 132 CMD_EXEC_WIREWATCH ("wirewatch-1"), 133 TALER_TESTING_cmd_reserve_poll_finish ( 134 "finish-poll-reserve-1", 135 GNUNET_TIME_UNIT_SECONDS, 136 "poll-reserve-1"), 137 /** 138 * Withdraw EUR:5. 139 */ 140 TALER_TESTING_cmd_withdraw_amount ( 141 "withdraw-coin-1", 142 "create-reserve-1", 143 "EUR:5", 144 0, /* age restriction off */ 145 MHD_HTTP_OK), 146 /** 147 * Check the reserve is depleted. 148 */ 149 TALER_TESTING_cmd_status ( 150 "status-1", 151 "create-reserve-1", 152 "EUR:0.03", 153 MHD_HTTP_OK), 154 TALER_TESTING_cmd_end () 155 }; 156 struct TALER_TESTING_Command push[] = { 157 TALER_TESTING_cmd_purse_create_with_deposit ( 158 "purse-with-deposit-for-delete", 159 MHD_HTTP_OK, 160 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 161 true, /* upload contract */ 162 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 163 "withdraw-coin-1", 164 "EUR:1.01", 165 NULL), 166 TALER_TESTING_cmd_purse_delete ( 167 "purse-with-deposit-delete", 168 MHD_HTTP_NO_CONTENT, 169 "purse-with-deposit-for-delete"), 170 TALER_TESTING_cmd_purse_create_with_deposit ( 171 "purse-with-deposit", 172 MHD_HTTP_OK, 173 "{\"amount\":\"EUR:0.99\",\"summary\":\"ice cream\"}", 174 true, /* upload contract */ 175 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 176 "withdraw-coin-1", 177 "EUR:1.00", 178 NULL), 179 TALER_TESTING_cmd_purse_poll ( 180 "push-poll-purse-before-merge", 181 MHD_HTTP_OK, 182 "purse-with-deposit", 183 "EUR:0.99", 184 true, 185 GNUNET_TIME_UNIT_MINUTES), 186 TALER_TESTING_cmd_contract_get ( 187 "push-get-contract", 188 MHD_HTTP_OK, 189 true, /* for merge */ 190 "purse-with-deposit"), 191 TALER_TESTING_cmd_purse_merge ( 192 "purse-merge-into-reserve", 193 MHD_HTTP_OK, 194 "push-get-contract", 195 "create-reserve-1"), 196 TALER_TESTING_cmd_purse_poll_finish ( 197 "push-merge-purse-poll-finish", 198 GNUNET_TIME_relative_multiply ( 199 GNUNET_TIME_UNIT_SECONDS, 200 5), 201 "push-poll-purse-before-merge"), 202 TALER_TESTING_cmd_status ( 203 "push-check-post-merge-reserve-balance-get", 204 "create-reserve-1", 205 "EUR:1.02", 206 MHD_HTTP_OK), 207 /* POST history doesn't yet support P2P transfers */ 208 TALER_TESTING_cmd_reserve_history ( 209 "push-check-post-merge-reserve-balance-post", 210 "create-reserve-1", 211 "EUR:1.02", 212 MHD_HTTP_OK), 213 /* Test conflicting merge */ 214 TALER_TESTING_cmd_purse_merge ( 215 "purse-merge-into-reserve", 216 MHD_HTTP_CONFLICT, 217 "push-get-contract", 218 "create-reserve-2"), 219 220 TALER_TESTING_cmd_end () 221 }; 222 struct TALER_TESTING_Command pull[] = { 223 TALER_TESTING_cmd_purse_create_with_reserve ( 224 "purse-create-with-reserve", 225 MHD_HTTP_OK, 226 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 227 true /* upload contract */, 228 true /* pay purse fee */, 229 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 230 "create-reserve-1"), 231 TALER_TESTING_cmd_contract_get ( 232 "pull-get-contract", 233 MHD_HTTP_OK, 234 false, /* for deposit */ 235 "purse-create-with-reserve"), 236 TALER_TESTING_cmd_purse_poll ( 237 "pull-poll-purse-before-deposit", 238 MHD_HTTP_OK, 239 "purse-create-with-reserve", 240 "EUR:1", 241 false, 242 GNUNET_TIME_UNIT_MINUTES), 243 TALER_TESTING_cmd_purse_deposit_coins ( 244 "purse-deposit-coins", 245 MHD_HTTP_OK, 246 0 /* min age */, 247 "purse-create-with-reserve", 248 "withdraw-coin-1", 249 "EUR:1.01", 250 NULL), 251 TALER_TESTING_cmd_purse_poll_finish ( 252 "pull-deposit-purse-poll-finish", 253 GNUNET_TIME_relative_multiply ( 254 GNUNET_TIME_UNIT_SECONDS, 255 5), 256 "pull-poll-purse-before-deposit"), 257 TALER_TESTING_cmd_status ( 258 "pull-check-post-merge-reserve-balance-get", 259 "create-reserve-1", 260 "EUR:2.02", 261 MHD_HTTP_OK), 262 TALER_TESTING_cmd_reserve_history ( 263 "push-check-post-merge-reserve-balance-post", 264 "create-reserve-1", 265 "EUR:2.02", 266 MHD_HTTP_OK), 267 TALER_TESTING_cmd_purse_deposit_coins ( 268 "purse-deposit-coins-idempotent", 269 MHD_HTTP_OK, 270 0 /* min age */, 271 "purse-create-with-reserve", 272 "withdraw-coin-1", 273 "EUR:1.01", 274 NULL), 275 /* create 2nd purse for a deposit conflict */ 276 TALER_TESTING_cmd_purse_create_with_reserve ( 277 "purse-create-with-reserve-2", 278 MHD_HTTP_OK, 279 "{\"amount\":\"EUR:4\",\"summary\":\"beer\"}", 280 true /* upload contract */, 281 true /* pay purse fee */, 282 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 283 "create-reserve-1"), 284 TALER_TESTING_cmd_purse_deposit_coins ( 285 "purse-deposit-coins-conflict", 286 MHD_HTTP_CONFLICT, 287 0 /* min age */, 288 "purse-create-with-reserve-2", 289 "withdraw-coin-1", 290 "EUR:4.01", 291 NULL), 292 TALER_TESTING_cmd_end () 293 }; 294 295 struct TALER_TESTING_Command expire[] = { 296 TALER_TESTING_cmd_purse_create_with_reserve ( 297 "purse-create-with-reserve-expire", 298 MHD_HTTP_OK, 299 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 300 true /* upload contract */, 301 true /* pay purse fee */, 302 GNUNET_TIME_relative_multiply ( 303 GNUNET_TIME_UNIT_SECONDS, 304 1), /* expiration */ 305 "create-reserve-1"), 306 TALER_TESTING_cmd_purse_poll ( 307 "pull-poll-purse-before-expire", 308 MHD_HTTP_GONE, 309 "purse-create-with-reserve-expire", 310 "EUR:1", 311 false, 312 GNUNET_TIME_UNIT_MINUTES), 313 TALER_TESTING_cmd_purse_create_with_deposit ( 314 "purse-with-deposit-expire", 315 MHD_HTTP_OK, 316 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 317 true, /* upload contract */ 318 GNUNET_TIME_relative_multiply ( 319 GNUNET_TIME_UNIT_SECONDS, 320 1), /* expiration */ 321 "withdraw-coin-1", 322 "EUR:1.02", 323 NULL), 324 TALER_TESTING_cmd_purse_poll ( 325 "push-poll-purse-before-expire", 326 MHD_HTTP_GONE, 327 "purse-with-deposit-expire", 328 "EUR:1", 329 true, /* wait for merge */ 330 GNUNET_TIME_UNIT_MINUTES), 331 /* This should fail, as too much of the coin 332 is already spend / in a purse */ 333 TALER_TESTING_cmd_purse_create_with_deposit ( 334 "purse-with-deposit-overspending", 335 MHD_HTTP_CONFLICT, 336 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 337 true, /* upload contract */ 338 GNUNET_TIME_relative_multiply ( 339 GNUNET_TIME_UNIT_SECONDS, 340 1), /* expiration */ 341 "withdraw-coin-1", 342 "EUR:2.01", 343 NULL), 344 TALER_TESTING_cmd_sleep ( 345 "sleep", 346 2 /* seconds */), 347 TALER_TESTING_cmd_exec_expire ( 348 "exec-expire", 349 config_file), 350 TALER_TESTING_cmd_purse_poll_finish ( 351 "push-merge-purse-poll-finish-expire", 352 GNUNET_TIME_relative_multiply ( 353 GNUNET_TIME_UNIT_SECONDS, 354 15), 355 "push-poll-purse-before-expire"), 356 TALER_TESTING_cmd_purse_poll_finish ( 357 "pull-deposit-purse-poll-expire-finish", 358 GNUNET_TIME_relative_multiply ( 359 GNUNET_TIME_UNIT_SECONDS, 360 15), 361 "pull-poll-purse-before-expire"), 362 /* coin was refunded, so now this should be OK */ 363 /* This should fail, as too much of the coin 364 is already spend / in a purse */ 365 TALER_TESTING_cmd_purse_create_with_deposit ( 366 "purse-with-deposit-refunded", 367 MHD_HTTP_OK, 368 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 369 true, /* upload contract */ 370 GNUNET_TIME_relative_multiply ( 371 GNUNET_TIME_UNIT_SECONDS, 372 1), /* expiration */ 373 "withdraw-coin-1", 374 "EUR:2.01", 375 NULL), 376 TALER_TESTING_cmd_end () 377 }; 378 struct TALER_TESTING_Command reserves[] = { 379 CMD_TRANSFER_TO_EXCHANGE ( 380 "create-reserve-100", 381 "EUR:1.04"), 382 TALER_TESTING_cmd_check_bank_admin_transfer ( 383 "check-create-reserve-100", 384 "EUR:1.04", 385 cred.user42_payto, 386 cred.exchange_payto, 387 "create-reserve-100"), 388 CMD_TRANSFER_TO_EXCHANGE ( 389 "create-reserve-101", 390 "EUR:1.04"), 391 TALER_TESTING_cmd_check_bank_admin_transfer ( 392 "check-create-reserve-101", 393 "EUR:1.04", 394 cred.user42_payto, 395 cred.exchange_payto, 396 "create-reserve-101"), 397 CMD_EXEC_WIREWATCH ("wirewatch-100"), 398 TALER_TESTING_cmd_withdraw_amount ( 399 "withdraw-coin-100", 400 "create-reserve-100", 401 "EUR:1", 402 0, /* age restriction off */ 403 MHD_HTTP_OK), 404 TALER_TESTING_cmd_reserve_open ( 405 "reserve-open-101-fail", 406 "create-reserve-101", 407 "EUR:0", 408 GNUNET_TIME_UNIT_YEARS, 409 5, /* min purses */ 410 MHD_HTTP_PAYMENT_REQUIRED, 411 NULL, 412 NULL), 413 TALER_TESTING_cmd_reserve_open ( 414 "reserve-open-101-ok-a", 415 "create-reserve-101", 416 "EUR:0.01", 417 GNUNET_TIME_UNIT_MONTHS, 418 1, /* min purses */ 419 MHD_HTTP_OK, 420 NULL, 421 NULL), 422 TALER_TESTING_cmd_status ( 423 "status-101-open-paid", 424 "create-reserve-101", 425 "EUR:1.03", 426 MHD_HTTP_OK), 427 TALER_TESTING_cmd_reserve_open ( 428 "reserve-open-101-ok-b", 429 "create-reserve-101", 430 "EUR:0", 431 GNUNET_TIME_UNIT_MONTHS, 432 2, /* min purses */ 433 MHD_HTTP_OK, 434 "withdraw-coin-100", 435 "EUR:0.03", /* 0.02 for the reserve open, 0.01 for deposit fee */ 436 NULL, 437 NULL), 438 /* Use purse creation with purse quota here */ 439 TALER_TESTING_cmd_purse_create_with_reserve ( 440 "purse-create-with-reserve-101-a", 441 MHD_HTTP_OK, 442 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 443 true /* upload contract */, 444 false /* pay purse fee */, 445 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 446 "create-reserve-101"), 447 TALER_TESTING_cmd_purse_create_with_reserve ( 448 "purse-create-with-reserve-101-b", 449 MHD_HTTP_OK, 450 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 451 true /* upload contract */, 452 false /* pay purse fee */, 453 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 454 "create-reserve-101"), 455 TALER_TESTING_cmd_purse_create_with_reserve ( 456 "purse-create-with-reserve-101-fail", 457 MHD_HTTP_CONFLICT, 458 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 459 true /* upload contract */, 460 false /* pay purse fee */, 461 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 462 "create-reserve-101"), 463 TALER_TESTING_cmd_reserve_get_attestable ( 464 "reserve-101-attestable", 465 "create-reserve-101", 466 MHD_HTTP_NOT_FOUND, 467 NULL), 468 TALER_TESTING_cmd_reserve_get_attestable ( 469 "reserve-101-attest", 470 "create-reserve-101", 471 MHD_HTTP_NOT_FOUND, 472 "nx-attribute-name", 473 NULL), 474 TALER_TESTING_cmd_oauth_with_birthdate ( 475 "start-oauth-service", 476 "2015-00-00", 477 6666), 478 TALER_TESTING_cmd_reserve_close ( 479 "reserve-101-close-kyc", 480 "create-reserve-101", 481 /* 44 => not to origin */ 482 cred.user44_payto, 483 MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), 484 TALER_TESTING_cmd_admin_add_kycauth ( 485 "setup-account-key", 486 "EUR:0.01", 487 &cred.ba, 488 cred.user44_payto, 489 NULL /* create new key */), 490 CMD_EXEC_WIREWATCH ( 491 "import-kyc-account"), 492 TALER_TESTING_cmd_check_kyc_get ( 493 "check-kyc-close-pending", 494 "reserve-101-close-kyc", 495 "setup-account-key", 496 TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, 497 MHD_HTTP_ACCEPTED), 498 TALER_TESTING_cmd_get_kyc_info ( 499 "get-kyc-info", 500 "check-kyc-close-pending", 501 MHD_HTTP_OK), 502 TALER_TESTING_cmd_post_kyc_start ( 503 "start-kyc-process", 504 "get-kyc-info", 505 0, 506 MHD_HTTP_OK), 507 TALER_TESTING_cmd_proof_kyc_oauth2 ( 508 "proof-close-kyc", 509 "reserve-101-close-kyc", 510 "test-oauth2", 511 "pass", 512 MHD_HTTP_SEE_OTHER), 513 TALER_TESTING_cmd_check_kyc_get ( 514 "check-kyc-close-ok", 515 "reserve-101-close-kyc", 516 "setup-account-key", 517 TALER_EXCHANGE_KLPT_KYC_OK, 518 MHD_HTTP_OK), 519 /* Now it should pass */ 520 TALER_TESTING_cmd_reserve_close ( 521 "reserve-101-close", 522 "create-reserve-101", 523 /* 44 => not to origin */ 524 cred.user44_payto, 525 MHD_HTTP_OK), 526 TALER_TESTING_cmd_exec_closer ( 527 "close-reserves-101", 528 config_file, 529 "EUR:1.02", 530 "EUR:0.01", 531 "create-reserve-101"), 532 TALER_TESTING_cmd_exec_transfer ( 533 "close-reserves-101-transfer", 534 config_file), 535 TALER_TESTING_cmd_status ( 536 "reserve-101-closed-status", 537 "create-reserve-101", 538 "EUR:0", 539 MHD_HTTP_OK), 540 TALER_TESTING_cmd_end () 541 }; 542 543 struct TALER_TESTING_Command commands[] = { 544 TALER_TESTING_cmd_run_fakebank ("run-fakebank", 545 cred.cfg, 546 "exchange-account-2"), 547 TALER_TESTING_cmd_system_start ("start-taler", 548 config_file, 549 "-e", 550 NULL), 551 TALER_TESTING_cmd_get_exchange ("get-exchange", 552 cred.cfg, 553 NULL, 554 true, 555 true), 556 TALER_TESTING_cmd_batch ("withdraw", 557 withdraw), 558 TALER_TESTING_cmd_batch ("push", 559 push), 560 TALER_TESTING_cmd_batch ("pull", 561 pull), 562 TALER_TESTING_cmd_batch ("expire", 563 expire), 564 TALER_TESTING_cmd_batch ("reserves", 565 reserves), 566 /* End the suite. */ 567 TALER_TESTING_cmd_end () 568 }; 569 570 (void) cls; 571 TALER_TESTING_run (is, 572 commands); 573 } 574 575 576 int 577 main (int argc, 578 char *const *argv) 579 { 580 (void) argc; 581 { 582 char *cipher; 583 584 cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); 585 GNUNET_assert (NULL != cipher); 586 uses_cs = (0 == strcmp (cipher, "cs")); 587 GNUNET_asprintf (&config_file, 588 "test_exchange_api-%s.conf", 589 cipher); 590 GNUNET_free (cipher); 591 } 592 return TALER_TESTING_main (argv, 593 "INFO", 594 config_file, 595 "exchange-account-2", 596 TALER_TESTING_BS_FAKEBANK, 597 &cred, 598 &run, 599 NULL); 600 } 601 602 603 /* end of test_exchange_p2p.c */