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