test_exchange_p2p.c (19518B)
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 /** 221 * A purse expiring after PURSE_EXPIRATION_LIMIT is refused: it would 222 * refund its coins at a point where the auditor can no longer account 223 * for the refund. The limit defaults to the shortest DURATION_LEGAL 224 * of any denomination, which is 3 years in the test configuration. 225 */ 226 TALER_TESTING_cmd_purse_create_with_deposit ( 227 "purse-with-deposit-expiring-after-limit", 228 MHD_HTTP_BAD_REQUEST, 229 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 230 true, /* upload contract */ 231 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 232 4), /* expiration */ 233 "withdraw-coin-1", 234 "EUR:1.01", 235 NULL), 236 TALER_TESTING_cmd_end () 237 }; 238 struct TALER_TESTING_Command pull[] = { 239 /** 240 * The same limit applies to a purse created together with its merge: 241 * such a purse also refunds the coins deposited into it if it is not 242 * paid in full by the time it expires. 243 */ 244 TALER_TESTING_cmd_purse_create_with_reserve ( 245 "purse-create-with-reserve-expiring-after-limit", 246 MHD_HTTP_BAD_REQUEST, 247 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 248 true /* upload contract */, 249 true /* pay purse fee */, 250 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 251 4), /* expiration */ 252 "create-reserve-1"), 253 TALER_TESTING_cmd_purse_create_with_reserve ( 254 "purse-create-with-reserve", 255 MHD_HTTP_OK, 256 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 257 true /* upload contract */, 258 true /* pay purse fee */, 259 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 260 "create-reserve-1"), 261 TALER_TESTING_cmd_contract_get ( 262 "pull-get-contract", 263 MHD_HTTP_OK, 264 false, /* for deposit */ 265 "purse-create-with-reserve"), 266 TALER_TESTING_cmd_purse_poll ( 267 "pull-poll-purse-before-deposit", 268 MHD_HTTP_OK, 269 "purse-create-with-reserve", 270 "EUR:1", 271 false, 272 GNUNET_TIME_UNIT_MINUTES), 273 TALER_TESTING_cmd_purse_deposit_coins ( 274 "purse-deposit-coins", 275 MHD_HTTP_OK, 276 0 /* min age */, 277 "purse-create-with-reserve", 278 "withdraw-coin-1", 279 "EUR:1.01", 280 NULL), 281 TALER_TESTING_cmd_purse_poll_finish ( 282 "pull-deposit-purse-poll-finish", 283 GNUNET_TIME_relative_multiply ( 284 GNUNET_TIME_UNIT_SECONDS, 285 5), 286 "pull-poll-purse-before-deposit"), 287 TALER_TESTING_cmd_status ( 288 "pull-check-post-merge-reserve-balance-get", 289 "create-reserve-1", 290 "EUR:2.02", 291 MHD_HTTP_OK), 292 TALER_TESTING_cmd_reserve_history ( 293 "push-check-post-merge-reserve-balance-post", 294 "create-reserve-1", 295 "EUR:2.02", 296 MHD_HTTP_OK), 297 TALER_TESTING_cmd_purse_deposit_coins ( 298 "purse-deposit-coins-idempotent", 299 MHD_HTTP_OK, 300 0 /* min age */, 301 "purse-create-with-reserve", 302 "withdraw-coin-1", 303 "EUR:1.01", 304 NULL), 305 /* create 2nd purse for a deposit conflict */ 306 TALER_TESTING_cmd_purse_create_with_reserve ( 307 "purse-create-with-reserve-2", 308 MHD_HTTP_OK, 309 "{\"amount\":\"EUR:4\",\"summary\":\"beer\"}", 310 true /* upload contract */, 311 true /* pay purse fee */, 312 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 313 "create-reserve-1"), 314 TALER_TESTING_cmd_purse_deposit_coins ( 315 "purse-deposit-coins-conflict", 316 MHD_HTTP_CONFLICT, 317 0 /* min age */, 318 "purse-create-with-reserve-2", 319 "withdraw-coin-1", 320 "EUR:4.01", 321 NULL), 322 TALER_TESTING_cmd_end () 323 }; 324 325 struct TALER_TESTING_Command expire[] = { 326 TALER_TESTING_cmd_purse_create_with_reserve ( 327 "purse-create-with-reserve-expire", 328 MHD_HTTP_OK, 329 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 330 true /* upload contract */, 331 true /* pay purse fee */, 332 GNUNET_TIME_relative_multiply ( 333 GNUNET_TIME_UNIT_SECONDS, 334 1), /* expiration */ 335 "create-reserve-1"), 336 TALER_TESTING_cmd_purse_poll ( 337 "pull-poll-purse-before-expire", 338 MHD_HTTP_GONE, 339 "purse-create-with-reserve-expire", 340 "EUR:1", 341 false, 342 GNUNET_TIME_UNIT_MINUTES), 343 TALER_TESTING_cmd_purse_create_with_deposit ( 344 "purse-with-deposit-expire", 345 MHD_HTTP_OK, 346 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 347 true, /* upload contract */ 348 GNUNET_TIME_relative_multiply ( 349 GNUNET_TIME_UNIT_SECONDS, 350 1), /* expiration */ 351 "withdraw-coin-1", 352 "EUR:1.02", 353 NULL), 354 TALER_TESTING_cmd_purse_poll ( 355 "push-poll-purse-before-expire", 356 MHD_HTTP_GONE, 357 "purse-with-deposit-expire", 358 "EUR:1", 359 true, /* wait for merge */ 360 GNUNET_TIME_UNIT_MINUTES), 361 /* This should fail, as too much of the coin 362 is already spend / in a purse */ 363 TALER_TESTING_cmd_purse_create_with_deposit ( 364 "purse-with-deposit-overspending", 365 MHD_HTTP_CONFLICT, 366 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 367 true, /* upload contract */ 368 GNUNET_TIME_relative_multiply ( 369 GNUNET_TIME_UNIT_SECONDS, 370 1), /* expiration */ 371 "withdraw-coin-1", 372 "EUR:2.01", 373 NULL), 374 TALER_TESTING_cmd_sleep ( 375 "sleep", 376 2 /* seconds */), 377 TALER_TESTING_cmd_exec_expire ( 378 "exec-expire", 379 config_file), 380 TALER_TESTING_cmd_purse_poll_finish ( 381 "push-merge-purse-poll-finish-expire", 382 GNUNET_TIME_relative_multiply ( 383 GNUNET_TIME_UNIT_SECONDS, 384 15), 385 "push-poll-purse-before-expire"), 386 TALER_TESTING_cmd_purse_poll_finish ( 387 "pull-deposit-purse-poll-expire-finish", 388 GNUNET_TIME_relative_multiply ( 389 GNUNET_TIME_UNIT_SECONDS, 390 15), 391 "pull-poll-purse-before-expire"), 392 /* coin was refunded, so now this should be OK */ 393 /* This should fail, as too much of the coin 394 is already spend / in a purse */ 395 TALER_TESTING_cmd_purse_create_with_deposit ( 396 "purse-with-deposit-refunded", 397 MHD_HTTP_OK, 398 "{\"amount\":\"EUR:2\",\"summary\":\"ice cream\"}", 399 true, /* upload contract */ 400 GNUNET_TIME_relative_multiply ( 401 GNUNET_TIME_UNIT_SECONDS, 402 1), /* expiration */ 403 "withdraw-coin-1", 404 "EUR:2.01", 405 NULL), 406 TALER_TESTING_cmd_end () 407 }; 408 struct TALER_TESTING_Command reserves[] = { 409 CMD_TRANSFER_TO_EXCHANGE ( 410 "create-reserve-100", 411 "EUR:1.04"), 412 TALER_TESTING_cmd_check_bank_admin_transfer ( 413 "check-create-reserve-100", 414 "EUR:1.04", 415 cred.user42_payto, 416 cred.exchange_payto, 417 "create-reserve-100"), 418 CMD_TRANSFER_TO_EXCHANGE ( 419 "create-reserve-101", 420 "EUR:1.04"), 421 TALER_TESTING_cmd_check_bank_admin_transfer ( 422 "check-create-reserve-101", 423 "EUR:1.04", 424 cred.user42_payto, 425 cred.exchange_payto, 426 "create-reserve-101"), 427 CMD_EXEC_WIREWATCH ("wirewatch-100"), 428 TALER_TESTING_cmd_withdraw_amount ( 429 "withdraw-coin-100", 430 "create-reserve-100", 431 "EUR:1", 432 0, /* age restriction off */ 433 MHD_HTTP_OK), 434 TALER_TESTING_cmd_reserve_open ( 435 "reserve-open-101-fail", 436 "create-reserve-101", 437 "EUR:0", 438 GNUNET_TIME_UNIT_YEARS, 439 5, /* min purses */ 440 MHD_HTTP_PAYMENT_REQUIRED, 441 NULL, 442 NULL), 443 TALER_TESTING_cmd_reserve_open ( 444 "reserve-open-101-ok-a", 445 "create-reserve-101", 446 "EUR:0.01", 447 GNUNET_TIME_UNIT_MONTHS, 448 1, /* min purses */ 449 MHD_HTTP_OK, 450 NULL, 451 NULL), 452 TALER_TESTING_cmd_status ( 453 "status-101-open-paid", 454 "create-reserve-101", 455 "EUR:1.03", 456 MHD_HTTP_OK), 457 TALER_TESTING_cmd_reserve_open ( 458 "reserve-open-101-ok-b", 459 "create-reserve-101", 460 "EUR:0", 461 GNUNET_TIME_UNIT_MONTHS, 462 2, /* min purses */ 463 MHD_HTTP_OK, 464 "withdraw-coin-100", 465 "EUR:0.03", /* 0.02 for the reserve open, 0.01 for deposit fee */ 466 NULL, 467 NULL), 468 /* Use purse creation with purse quota here */ 469 TALER_TESTING_cmd_purse_create_with_reserve ( 470 "purse-create-with-reserve-101-a", 471 MHD_HTTP_OK, 472 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 473 true /* upload contract */, 474 false /* pay purse fee */, 475 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 476 "create-reserve-101"), 477 TALER_TESTING_cmd_purse_create_with_reserve ( 478 "purse-create-with-reserve-101-b", 479 MHD_HTTP_OK, 480 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 481 true /* upload contract */, 482 false /* pay purse fee */, 483 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 484 "create-reserve-101"), 485 TALER_TESTING_cmd_purse_create_with_reserve ( 486 "purse-create-with-reserve-101-fail", 487 MHD_HTTP_CONFLICT, 488 "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", 489 true /* upload contract */, 490 false /* pay purse fee */, 491 GNUNET_TIME_UNIT_MINUTES, /* expiration */ 492 "create-reserve-101"), 493 TALER_TESTING_cmd_reserve_get_attestable ( 494 "reserve-101-attestable", 495 "create-reserve-101", 496 MHD_HTTP_NOT_FOUND, 497 NULL), 498 TALER_TESTING_cmd_reserve_get_attestable ( 499 "reserve-101-attest", 500 "create-reserve-101", 501 MHD_HTTP_NOT_FOUND, 502 "nx-attribute-name", 503 NULL), 504 TALER_TESTING_cmd_oauth_with_birthdate ( 505 "start-oauth-service", 506 "2015-00-00", 507 6666), 508 TALER_TESTING_cmd_reserve_close ( 509 "reserve-101-close-kyc", 510 "create-reserve-101", 511 /* 44 => not to origin */ 512 cred.user44_payto, 513 MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), 514 TALER_TESTING_cmd_admin_add_kycauth ( 515 "setup-account-key", 516 "EUR:0.01", 517 &cred.ba, 518 cred.user44_payto, 519 NULL /* create new key */), 520 CMD_EXEC_WIREWATCH ( 521 "import-kyc-account"), 522 TALER_TESTING_cmd_check_kyc_get ( 523 "check-kyc-close-pending", 524 "reserve-101-close-kyc", 525 "setup-account-key", 526 TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER, 527 MHD_HTTP_ACCEPTED), 528 TALER_TESTING_cmd_get_kyc_info ( 529 "get-kyc-info", 530 "check-kyc-close-pending", 531 MHD_HTTP_OK), 532 TALER_TESTING_cmd_post_kyc_start ( 533 "start-kyc-process", 534 "get-kyc-info", 535 0, 536 MHD_HTTP_OK), 537 TALER_TESTING_cmd_proof_kyc_oauth2 ( 538 "proof-close-kyc", 539 "start-kyc-process", 540 "test-oauth2", 541 "pass", 542 MHD_HTTP_SEE_OTHER), 543 TALER_TESTING_cmd_check_kyc_get ( 544 "check-kyc-close-ok", 545 "reserve-101-close-kyc", 546 "setup-account-key", 547 TALER_EXCHANGE_KLPT_KYC_OK, 548 MHD_HTTP_OK), 549 /* Now it should pass */ 550 TALER_TESTING_cmd_reserve_close ( 551 "reserve-101-close", 552 "create-reserve-101", 553 /* 44 => not to origin */ 554 cred.user44_payto, 555 MHD_HTTP_OK), 556 TALER_TESTING_cmd_exec_closer ( 557 "close-reserves-101", 558 config_file, 559 "EUR:1.02", 560 "EUR:0.01", 561 "create-reserve-101"), 562 TALER_TESTING_cmd_exec_transfer ( 563 "close-reserves-101-transfer", 564 config_file), 565 TALER_TESTING_cmd_status ( 566 "reserve-101-closed-status", 567 "create-reserve-101", 568 "EUR:0", 569 MHD_HTTP_OK), 570 TALER_TESTING_cmd_end () 571 }; 572 573 struct TALER_TESTING_Command commands[] = { 574 TALER_TESTING_cmd_run_fakebank ("run-fakebank", 575 cred.cfg, 576 "exchange-account-2"), 577 TALER_TESTING_cmd_system_start ("start-taler", 578 config_file, 579 "-e", 580 NULL), 581 TALER_TESTING_cmd_get_exchange ("get-exchange", 582 cred.cfg, 583 NULL, 584 true, 585 true), 586 TALER_TESTING_cmd_batch ("withdraw", 587 withdraw), 588 TALER_TESTING_cmd_batch ("push", 589 push), 590 TALER_TESTING_cmd_batch ("pull", 591 pull), 592 TALER_TESTING_cmd_batch ("expire", 593 expire), 594 TALER_TESTING_cmd_batch ("reserves", 595 reserves), 596 /* End the suite. */ 597 TALER_TESTING_cmd_end () 598 }; 599 600 (void) cls; 601 TALER_TESTING_run (is, 602 commands); 603 } 604 605 606 int 607 main (int argc, 608 char *const *argv) 609 { 610 (void) argc; 611 { 612 char *cipher; 613 614 cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); 615 GNUNET_assert (NULL != cipher); 616 uses_cs = (0 == strcmp (cipher, "cs")); 617 GNUNET_asprintf (&config_file, 618 "test_exchange_api-%s.conf", 619 cipher); 620 GNUNET_free (cipher); 621 } 622 return TALER_TESTING_main (argv, 623 "INFO", 624 config_file, 625 "exchange-account-2", 626 TALER_TESTING_BS_FAKEBANK, 627 &cred, 628 &run, 629 NULL); 630 } 631 632 633 /* end of test_exchange_p2p.c */