Line data Source code
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 "platform.h"
25 : #include "taler_attributes.h"
26 : #include "taler_util.h"
27 : #include "taler_signatures.h"
28 : #include "taler_exchange_service.h"
29 : #include "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_bank_service.h"
34 : #include "taler_fakebank_lib.h"
35 : #include "taler_testing_lib.h"
36 : #include "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 2 : 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 2 : CMD_TRANSFER_TO_EXCHANGE (
111 : "create-reserve-1",
112 : "EUR:5.04"),
113 2 : CMD_TRANSFER_TO_EXCHANGE (
114 : "create-reserve-2",
115 : "EUR:5.01"),
116 2 : 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 2 : 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 2 : 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 2 : CMD_EXEC_WIREWATCH ("wirewatch-1"),
139 2 : 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 2 : 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 2 : TALER_TESTING_cmd_status (
156 : "status-1",
157 : "create-reserve-1",
158 : "EUR:0.03",
159 : MHD_HTTP_OK),
160 2 : TALER_TESTING_cmd_end ()
161 : };
162 : struct TALER_TESTING_Command push[] = {
163 2 : 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 2 : TALER_TESTING_cmd_purse_delete (
173 : "purse-with-deposit-delete",
174 : MHD_HTTP_NO_CONTENT,
175 : "purse-with-deposit-for-delete"),
176 2 : 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 2 : 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 2 : TALER_TESTING_cmd_contract_get (
193 : "push-get-contract",
194 : MHD_HTTP_OK,
195 : true, /* for merge */
196 : "purse-with-deposit"),
197 2 : TALER_TESTING_cmd_purse_merge (
198 : "purse-merge-into-reserve",
199 : MHD_HTTP_OK,
200 : "push-get-contract",
201 : "create-reserve-1"),
202 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_purse_merge (
221 : "purse-merge-into-reserve",
222 : MHD_HTTP_CONFLICT,
223 : "push-get-contract",
224 : "create-reserve-2"),
225 :
226 2 : TALER_TESTING_cmd_end ()
227 : };
228 : struct TALER_TESTING_Command pull[] = {
229 2 : 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 2 : TALER_TESTING_cmd_contract_get (
238 : "pull-get-contract",
239 : MHD_HTTP_OK,
240 : false, /* for deposit */
241 : "purse-create-with-reserve"),
242 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_end ()
299 : };
300 :
301 : struct TALER_TESTING_Command expire[] = {
302 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_sleep (
351 : "sleep",
352 : 2 /* seconds */),
353 2 : TALER_TESTING_cmd_exec_expire (
354 : "exec-expire",
355 : config_file),
356 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_end ()
383 : };
384 : struct TALER_TESTING_Command reserves[] = {
385 2 : CMD_TRANSFER_TO_EXCHANGE (
386 : "create-reserve-100",
387 : "EUR:1.04"),
388 2 : 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 2 : CMD_TRANSFER_TO_EXCHANGE (
395 : "create-reserve-101",
396 : "EUR:1.04"),
397 2 : 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 2 : CMD_EXEC_WIREWATCH ("wirewatch-100"),
404 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_status (
429 : "status-101-open-paid",
430 : "create-reserve-101",
431 : "EUR:1.03",
432 : MHD_HTTP_OK),
433 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_reserve_get_attestable (
470 : "reserve-101-attestable",
471 : "create-reserve-101",
472 : MHD_HTTP_NOT_FOUND,
473 : NULL),
474 2 : 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 2 : TALER_TESTING_cmd_oauth_with_birthdate (
481 : "start-oauth-service",
482 : "2015-00-00",
483 : 6666),
484 2 : 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 2 : 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 2 : CMD_EXEC_WIREWATCH (
497 : "import-kyc-account"),
498 2 : 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 2 : TALER_TESTING_cmd_get_kyc_info (
505 : "get-kyc-info",
506 : "check-kyc-close-pending",
507 : MHD_HTTP_OK),
508 2 : TALER_TESTING_cmd_post_kyc_start (
509 : "start-kyc-process",
510 : "get-kyc-info",
511 : 0,
512 : MHD_HTTP_OK),
513 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_exec_transfer (
539 : "close-reserves-101-transfer",
540 : config_file),
541 2 : TALER_TESTING_cmd_status (
542 : "reserve-101-closed-status",
543 : "create-reserve-101",
544 : "EUR:0",
545 : MHD_HTTP_OK),
546 2 : TALER_TESTING_cmd_end ()
547 : };
548 :
549 : struct TALER_TESTING_Command commands[] = {
550 2 : TALER_TESTING_cmd_run_fakebank ("run-fakebank",
551 2 : cred.cfg,
552 : "exchange-account-2"),
553 2 : TALER_TESTING_cmd_system_start ("start-taler",
554 : config_file,
555 : "-e",
556 : NULL),
557 2 : TALER_TESTING_cmd_get_exchange ("get-exchange",
558 2 : cred.cfg,
559 : NULL,
560 : true,
561 : true),
562 2 : TALER_TESTING_cmd_batch ("withdraw",
563 : withdraw),
564 2 : TALER_TESTING_cmd_batch ("push",
565 : push),
566 2 : TALER_TESTING_cmd_batch ("pull",
567 : pull),
568 2 : TALER_TESTING_cmd_batch ("expire",
569 : expire),
570 2 : TALER_TESTING_cmd_batch ("reserves",
571 : reserves),
572 : /* End the suite. */
573 2 : TALER_TESTING_cmd_end ()
574 : };
575 :
576 : (void) cls;
577 2 : TALER_TESTING_run (is,
578 : commands);
579 2 : }
580 :
581 :
582 : int
583 2 : main (int argc,
584 : char *const *argv)
585 : {
586 : (void) argc;
587 : {
588 : char *cipher;
589 :
590 2 : cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
591 2 : GNUNET_assert (NULL != cipher);
592 2 : uses_cs = (0 == strcmp (cipher, "cs"));
593 2 : GNUNET_asprintf (&config_file,
594 : "test_exchange_api-%s.conf",
595 : cipher);
596 2 : GNUNET_free (cipher);
597 : }
598 2 : 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 */
|