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