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 "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 2 : 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 2 : CMD_TRANSFER_TO_EXCHANGE (
105 : "create-reserve-1",
106 : "EUR:5.04"),
107 2 : CMD_TRANSFER_TO_EXCHANGE (
108 : "create-reserve-2",
109 : "EUR:5.01"),
110 2 : 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 2 : 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 2 : 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 2 : CMD_EXEC_WIREWATCH ("wirewatch-1"),
133 2 : 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 2 : 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 2 : TALER_TESTING_cmd_status (
150 : "status-1",
151 : "create-reserve-1",
152 : "EUR:0.03",
153 : MHD_HTTP_OK),
154 2 : TALER_TESTING_cmd_end ()
155 : };
156 : struct TALER_TESTING_Command push[] = {
157 2 : 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 2 : TALER_TESTING_cmd_purse_delete (
167 : "purse-with-deposit-delete",
168 : MHD_HTTP_NO_CONTENT,
169 : "purse-with-deposit-for-delete"),
170 2 : 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 2 : 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 2 : TALER_TESTING_cmd_contract_get (
187 : "push-get-contract",
188 : MHD_HTTP_OK,
189 : true, /* for merge */
190 : "purse-with-deposit"),
191 2 : TALER_TESTING_cmd_purse_merge (
192 : "purse-merge-into-reserve",
193 : MHD_HTTP_OK,
194 : "push-get-contract",
195 : "create-reserve-1"),
196 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_purse_merge (
215 : "purse-merge-into-reserve",
216 : MHD_HTTP_CONFLICT,
217 : "push-get-contract",
218 : "create-reserve-2"),
219 :
220 2 : TALER_TESTING_cmd_end ()
221 : };
222 : struct TALER_TESTING_Command pull[] = {
223 2 : 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 2 : TALER_TESTING_cmd_contract_get (
232 : "pull-get-contract",
233 : MHD_HTTP_OK,
234 : false, /* for deposit */
235 : "purse-create-with-reserve"),
236 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_end ()
293 : };
294 :
295 : struct TALER_TESTING_Command expire[] = {
296 2 : 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 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_sleep (
345 : "sleep",
346 : 2 /* seconds */),
347 2 : TALER_TESTING_cmd_exec_expire (
348 : "exec-expire",
349 : config_file),
350 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_end ()
377 : };
378 : struct TALER_TESTING_Command reserves[] = {
379 2 : CMD_TRANSFER_TO_EXCHANGE (
380 : "create-reserve-100",
381 : "EUR:1.04"),
382 2 : 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 2 : CMD_TRANSFER_TO_EXCHANGE (
389 : "create-reserve-101",
390 : "EUR:1.04"),
391 2 : 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 2 : CMD_EXEC_WIREWATCH ("wirewatch-100"),
398 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_status (
423 : "status-101-open-paid",
424 : "create-reserve-101",
425 : "EUR:1.03",
426 : MHD_HTTP_OK),
427 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_reserve_get_attestable (
464 : "reserve-101-attestable",
465 : "create-reserve-101",
466 : MHD_HTTP_NOT_FOUND,
467 : NULL),
468 2 : 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 2 : TALER_TESTING_cmd_oauth_with_birthdate (
475 : "start-oauth-service",
476 : "2015-00-00",
477 : 6666),
478 2 : 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 2 : 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 2 : CMD_EXEC_WIREWATCH (
491 : "import-kyc-account"),
492 2 : 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 2 : TALER_TESTING_cmd_get_kyc_info (
499 : "get-kyc-info",
500 : "check-kyc-close-pending",
501 : MHD_HTTP_OK),
502 2 : TALER_TESTING_cmd_post_kyc_start (
503 : "start-kyc-process",
504 : "get-kyc-info",
505 : 0,
506 : MHD_HTTP_OK),
507 2 : 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 2 : 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 2 : 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 2 : 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 2 : TALER_TESTING_cmd_exec_transfer (
533 : "close-reserves-101-transfer",
534 : config_file),
535 2 : TALER_TESTING_cmd_status (
536 : "reserve-101-closed-status",
537 : "create-reserve-101",
538 : "EUR:0",
539 : MHD_HTTP_OK),
540 2 : TALER_TESTING_cmd_end ()
541 : };
542 :
543 : struct TALER_TESTING_Command commands[] = {
544 2 : TALER_TESTING_cmd_run_fakebank ("run-fakebank",
545 2 : cred.cfg,
546 : "exchange-account-2"),
547 2 : TALER_TESTING_cmd_system_start ("start-taler",
548 : config_file,
549 : "-e",
550 : NULL),
551 2 : TALER_TESTING_cmd_get_exchange ("get-exchange",
552 2 : cred.cfg,
553 : NULL,
554 : true,
555 : true),
556 2 : TALER_TESTING_cmd_batch ("withdraw",
557 : withdraw),
558 2 : TALER_TESTING_cmd_batch ("push",
559 : push),
560 2 : TALER_TESTING_cmd_batch ("pull",
561 : pull),
562 2 : TALER_TESTING_cmd_batch ("expire",
563 : expire),
564 2 : TALER_TESTING_cmd_batch ("reserves",
565 : reserves),
566 : /* End the suite. */
567 2 : TALER_TESTING_cmd_end ()
568 : };
569 :
570 : (void) cls;
571 2 : TALER_TESTING_run (is,
572 : commands);
573 2 : }
574 :
575 :
576 : int
577 2 : main (int argc,
578 : char *const *argv)
579 : {
580 : (void) argc;
581 : {
582 : char *cipher;
583 :
584 2 : cipher = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]);
585 2 : GNUNET_assert (NULL != cipher);
586 2 : uses_cs = (0 == strcmp (cipher, "cs"));
587 2 : GNUNET_asprintf (&config_file,
588 : "test_exchange_api-%s.conf",
589 : cipher);
590 2 : GNUNET_free (cipher);
591 : }
592 2 : 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 */
|