aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2018-06-12 15:22:21 +0200
committerMarcello Stanisci <stanisci.m@gmail.com>2018-06-12 15:22:21 +0200
commit23290a28449f36371a5e94f0571fa7548170ffb7 (patch)
tree3ed7292f6d6508758b04eb25e18e728391a09329
parent6f7d5992698d57b4f3b3c079b4392b6dbc5c1b1b (diff)
downloadmerchant-23290a28449f36371a5e94f0571fa7548170ffb7.tar.gz
merchant-23290a28449f36371a5e94f0571fa7548170ffb7.zip
Renaming payment generator executable.
-rw-r--r--src/merchant-tools/Makefile.am4
-rw-r--r--src/merchant-tools/taler-merchant-generate-payments.c2437
-rw-r--r--src/merchant-tools/taler-merchant-generate-payments_new.c640
3 files changed, 530 insertions, 2551 deletions
diff --git a/src/merchant-tools/Makefile.am b/src/merchant-tools/Makefile.am
index 69354cd3..2a8ff2e8 100644
--- a/src/merchant-tools/Makefile.am
+++ b/src/merchant-tools/Makefile.am
@@ -15,8 +15,8 @@ taler_merchant_dbinit_LDADD = \
15 -ltalerutil \ 15 -ltalerutil \
16 -ltalerpq 16 -ltalerpq
17 17
18taler_merchant_generate_payments_new_SOURCES = \ 18taler_merchant_generate_payments_SOURCES = \
19 taler-merchant-generate-payments_new.c 19 taler-merchant-generate-payments.c
20 20
21taler_merchant_generate_payments_new_LDADD = \ 21taler_merchant_generate_payments_new_LDADD = \
22 $(top_srcdir)/src/backenddb/libtalermerchantdb.la \ 22 $(top_srcdir)/src/backenddb/libtalermerchantdb.la \
diff --git a/src/merchant-tools/taler-merchant-generate-payments.c b/src/merchant-tools/taler-merchant-generate-payments.c
index 33716b91..db27b212 100644
--- a/src/merchant-tools/taler-merchant-generate-payments.c
+++ b/src/merchant-tools/taler-merchant-generate-payments.c
@@ -1,2021 +1,640 @@
1/* 1/*
2 This file is part of TALER 2 This file is part of TALER
3 Copyright (C) 2014-2017 Taler Systems SA 3 (C) 2014-2018 Taler Systems SA
4 4
5 TALER is free software; you can redistribute it and/or modify it under the 5 TALER is free software; you can redistribute it and/or modify it
6 terms of the GNU Lesser General Public License as published by the Free Software 6 under the terms of the GNU Affero General Public License as
7 Foundation; either version 2.1, or (at your option) any later version. 7 published by the Free Software Foundation; either version 3, or
8 (at your option) any later version.
8 9
9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 TALER is distributed in the hope that it will be useful, but
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 WITHOUT ANY WARRANTY; without even the implied warranty of
11 A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
12 14
13 You should have received a copy of the GNU Lesser General Public License along with 15 You should have received a copy of the GNU General Public License
14 TALER; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/> 16 along with TALER; see the file COPYING. If not,
17 see <http://www.gnu.org/licenses/>
15*/ 18*/
19
16/** 20/**
17 * @file taler-merchant-generate-payments.c 21 * @file merchant/backend/taler-merchant-httpd.c
18 * @brief tool to use (or test) the taler backend by running some transactions 22 * @brief HTTP serving layer intended to perform crypto-work and
19 * @author Christian Grothoff 23 * communication with the exchange
20 * @author Marcello Stanisci 24 * @author Marcello Stanisci
21 *
22 * TODO:
23 * - trigger wirewatch after /admin/add/incoming!
24 */ 25 */
26
25#include "platform.h" 27#include "platform.h"
26#include <taler/taler_exchange_service.h>
27#include <taler/taler_bank_service.h>
28#include <taler/taler_fakebank_lib.h>
29#include <taler/taler_json_lib.h>
30#include <taler/taler_util.h> 28#include <taler/taler_util.h>
31#include <taler/taler_signatures.h> 29#include <taler/taler_signatures.h>
32#include "taler_merchant_service.h" 30#include <taler/taler_exchange_service.h>
33#include "taler_merchantdb_lib.h" 31#include <taler/taler_json_lib.h>
34#include <gnunet/gnunet_util_lib.h> 32#include <gnunet/gnunet_util_lib.h>
35#include <gnunet/gnunet_curl_lib.h>
36#include <microhttpd.h> 33#include <microhttpd.h>
34#include <taler/taler_bank_service.h>
35#include <taler/taler_fakebank_lib.h>
36#include <taler/taler_testing_lib.h>
37#include <taler/taler_testing_bank_lib.h>
38#include <taler/taler_error_codes.h>
39#include "taler_merchant_testing_lib.h"
40
41/* Error codes. */
42enum PaymentGeneratorError {
43
44 MISSING_MERCHANT_URL = 2,
45 FAILED_TO_LAUNCH_MERCHANT,
46 MISSING_BANK_URL,
47 FAILED_TO_LAUNCH_BANK,
48 BAD_CLI_ARG,
49 BAD_CONFIG_FILE
50};
37 51
38/** 52/* Hard-coded params. Note, the bank is expected to
39 * Number of the account the exchange has at the bank. 53 * have the Tor user with account number 3 and password 'x'.
40 */ 54 *
55 * This is not a problem _so far_, as the fakebank mocks logins,
56 * and the Python bank makes that account by default. */
57#define USER_ACCOUNT_NO 3
41#define EXCHANGE_ACCOUNT_NO 2 58#define EXCHANGE_ACCOUNT_NO 2
59#define USER_LOGIN_NAME "Tor"
60#define USER_LOGIN_PASS "x"
61#define EXCHANGE_URL "http://example.com/"
42 62
43/** 63#define FIRST_INSTRUCTION -1
44 * The exchange process launched by the generator 64#define TRACKS_INSTRUCTION 9
45 */
46static struct GNUNET_OS_Process *exchanged;
47 65
48/** 66#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \
49 * The merchant process launched by the generator 67 TALER_TESTING_cmd_fakebank_transfer (label, amount, \
50 */ 68 bank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
51static struct GNUNET_OS_Process *merchantd; 69 USER_LOGIN_NAME, USER_LOGIN_PASS, EXCHANGE_URL)
52 70
53/** 71/**
54 * How many times the command list should be rerun. 72 * Exit code.
55 */ 73 */
56static unsigned int times = 1; 74static unsigned int result;
57 75
58/** 76/**
59 * Current iteration of commands 77 * Bank process.
60 */ 78 */
61static unsigned int j = 0; 79static struct GNUNET_OS_Process *bankd;
62
63/**
64 * Indicates whether we use an external exchange.
65 * By default, the generator forks a local exchange.
66 */
67static int remote_bank = 0;
68
69/**
70 * Indicates whether we use an external exchange.
71 * By default, the generator forks a local exchange.
72 */
73static int remote_exchange = 0;
74
75/**
76 * Indicates whether we use an external merchant.
77 * By default, the generator tries to fork a local
78 * merchant.
79 */
80static int remote_merchant = 0;
81
82/**
83 * Exchange URL to withdraw from and deposit to.
84 */
85static char *exchange_url;
86
87/**
88 * Base URL of exchange's admin interface.
89 */
90static char *exchange_url_admin;
91
92/**
93 * Merchant backend to get proposals from and pay.
94 */
95static char *merchant_url;
96 80
97/** 81/**
98 * Customer's bank URL, communicated at withdrawal time 82 * Merchant process.
99 * to the exchange; must be the same as the exchange's bank.
100 */ 83 */
101static char *bank_url; 84static struct GNUNET_OS_Process *merchantd;
102
103/**
104 * Which merchant instance we use.
105 */
106static char *instance;
107
108/**
109 * Currency used to generate payments.
110 */
111static char *currency;
112
113/**
114 * Handle to our fakebank.
115 */
116static struct TALER_FAKEBANK_Handle *fakebank;
117
118/**
119 * Task run on timeout.
120 */
121static struct GNUNET_SCHEDULER_Task *timeout_task;
122
123/**
124 * Handle to access the exchange.
125 */
126static struct TALER_EXCHANGE_Handle *exchange;
127
128/**
129 * Main execution context for the main loop of the exchange.
130 */
131static struct GNUNET_CURL_Context *ctx;
132
133/**
134 * Context for running the #ctx's event loop.
135 */
136static struct GNUNET_CURL_RescheduleContext *rc;
137
138/**
139 * Result of the testcases, #GNUNET_OK on success.
140 */
141static int result;
142
143/**
144 * Pipe used to communicate child death via signal.
145 */
146static struct GNUNET_DISK_PipeHandle *sigpipe;
147
148/**
149 * Signal handler we overrode.
150 */
151static struct GNUNET_SIGNAL_Context *shc_chld;
152
153/**
154 * Name of the configuration file we are using.
155 */
156static char *cfgfilename;
157
158
159/**
160 * State of the interpreter loop.
161 */
162struct InterpreterState
163{
164 /**
165 * Keys from the exchange.
166 */
167 const struct TALER_EXCHANGE_Keys *keys;
168
169 /**
170 * Commands the interpreter will run.
171 */
172 struct Command *commands;
173
174 /**
175 * Interpreter task (if one is scheduled).
176 */
177 struct GNUNET_SCHEDULER_Task *task;
178
179 /**
180 * Instruction pointer. Tells #interpreter_run() which
181 * instruction to run next.
182 */
183 unsigned int ip;
184
185};
186
187/**
188 * Opcodes for the interpreter.
189 */
190enum OpCode
191{
192 /**
193 * Termination code, stops the interpreter loop (with success).
194 */
195 OC_END = 0,
196
197 /**
198 * Issue a GET /proposal to the backend.
199 */
200 OC_PROPOSAL_LOOKUP,
201
202 /**
203 * Add funds to a reserve by (faking) incoming wire transfer.
204 */
205 OC_ADMIN_ADD_INCOMING,
206
207 /**
208 * Run the wirewatcher to check for incoming transactions.
209 */
210 OC_RUN_WIREWATCH,
211
212 /**
213 * Check status of a reserve.
214 */
215 OC_WITHDRAW_STATUS,
216
217 /**
218 * Withdraw a coin from a reserve.
219 */
220 OC_WITHDRAW_SIGN,
221
222 /**
223 * Issue a PUT /proposal to the backend.
224 */
225 OC_PROPOSAL,
226
227 /**
228 * Pay with coins.
229 */
230 OC_PAY
231
232};
233
234
235/**
236 * Details for a exchange operation to execute.
237 */
238struct Command
239{
240 /**
241 * Opcode of the command.
242 */
243 enum OpCode oc;
244
245 /**
246 * Label for the command, can be NULL.
247 */
248 const char *label;
249
250 /**
251 * Which response code do we expect for this command?
252 */
253 unsigned int expected_response_code;
254
255 /**
256 * Details about the command.
257 */
258 union
259 {
260
261 /**
262 * Information for a #OC_WITHDRAW_SIGN command.
263 */
264 struct
265 {
266
267 /**
268 * Which reserve should we withdraw from?
269 */
270 const char *reserve_reference;
271
272 /**
273 * String describing the denomination value we should withdraw.
274 * A corresponding denomination key must exist in the exchange's
275 * offerings. Can be NULL if @e pk is set instead.
276 * The interpreter must free this value after it doesn't need it
277 * anymore.
278 */
279 char *amount;
280
281 /**
282 * If @e amount is NULL, this specifies the denomination key to
283 * use. Otherwise, this will be set (by the interpreter) to the
284 * denomination PK matching @e amount.
285 */
286 const struct TALER_EXCHANGE_DenomPublicKey *pk;
287
288 /**
289 * Set (by the interpreter) to the exchange's signature over the
290 * coin's public key.
291 */
292 struct TALER_DenominationSignature sig;
293
294 /**
295 * Secrets of the planchet. Set by the interpreter.
296 */
297 struct TALER_PlanchetSecretsP ps;
298
299 /**
300 * Withdraw handle (while operation is running).
301 */
302 struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
303
304 } reserve_withdraw;
305
306 /**
307 * Information for a #OC_ADMIN_ADD_INCOMING command.
308 */
309 struct
310 {
311
312 /**
313 * Label to another admin_add_incoming command if we
314 * should deposit into an existing reserve, NULL if
315 * a fresh reserve should be created.
316 */
317 const char *reserve_reference;
318
319 /**
320 * String describing the amount to add to the reserve.
321 */
322 char *amount;
323
324 /**
325 * Sender's bank account number.
326 */
327 uint64_t debit_account_no;
328
329 /**
330 * Receiver's bank account number.
331 */
332 uint64_t credit_account_no;
333
334 /**
335 * Set (by the interpreter) to the reserve's private key
336 * we used to fill the reserve.
337 */
338 struct TALER_ReservePrivateKeyP reserve_priv;
339
340 /**
341 * Set to the API's handle during the operation.
342 */
343 struct TALER_BANK_AdminAddIncomingHandle *aih;
344
345 /**
346 * Set to bank's identifier for the wire transfer.
347 */
348 uint64_t serial_id;
349
350 } admin_add_incoming;
351
352 struct {
353
354 /**
355 * Process for the wirewatcher.
356 */
357 struct GNUNET_OS_Process *wirewatch_proc;
358
359 /**
360 * ID of task called whenever we get a SIGCHILD.
361 */
362 struct GNUNET_SCHEDULER_Task *child_death_task;
363
364 } run_wirewatch;
365
366 /**
367 * Information for an #OC_PROPOSAL command.
368 */
369 struct
370 {
371
372 /**
373 * Max deposit fee accepted by the merchant.
374 * Given in the form "CURRENCY:X.Y".
375 */
376 char *max_fee;
377
378 /**
379 * Proposal overall price.
380 * Given in the form "CURRENCY:X.Y".
381 */
382 char *amount;
383
384 /**
385 * Handle to the active PUT /proposal operation, or NULL.
386 */
387 struct TALER_MERCHANT_ProposalOperation *po;
388
389 /**
390 * Handle to the active GET /proposal operation, or NULL
391 */
392 struct TALER_MERCHANT_ProposalLookupOperation *plo;
393
394 /**
395 * Full contract in JSON, set by the /contract operation.
396 * FIXME: verify in the code that this bit is actually proposal
397 * data and not the whole proposal.
398 */
399 json_t *contract_terms;
400
401 /**
402 * Proposal's signature.
403 */
404 struct TALER_MerchantSignatureP merchant_sig;
405
406 /**
407 * Proposal data's hashcode.
408 */
409 struct GNUNET_HashCode hash;
410
411 } proposal;
412
413 /**
414 * Information for a #OC_PAY command.
415 * FIXME: support tests where we pay with multiple coins at once.
416 */
417 struct
418 {
419
420 /**
421 * Reference to the contract.
422 */
423 const char *contract_ref;
424
425 /**
426 * Reference to a reserve_withdraw operation for a coin to
427 * be used for the /deposit operation.
428 */
429 const char *coin_ref;
430
431 /**
432 * If this @e coin_ref refers to an operation that generated
433 * an array of coins, this value determines which coin to use.
434 */
435 unsigned int coin_idx;
436
437 /**
438 * Amount to pay (from the coin, including fee).
439 */
440 char *amount_with_fee;
441
442 /**
443 * Amount to pay (from the coin, excluding fee). The sum of the
444 * deltas between all @e amount_with_fee and the @e
445 * amount_without_fee must be less than max_fee, and the sum of
446 * the @e amount_with_fee must be larger than the @e
447 * total_amount.
448 */
449 char *amount_without_fee;
450
451 /**
452 * Deposit handle while operation is running.
453 */
454 struct TALER_MERCHANT_Pay *ph;
455
456 /**
457 * Hashcode of the proposal data associated to this payment.
458 */
459 struct GNUNET_HashCode h_contract_terms;
460
461 /**
462 * Merchant's public key
463 */
464 struct TALER_MerchantPublicKeyP merchant_pub;
465
466 } pay;
467
468 } details;
469
470};
471
472 85
473/** 86/**
474 * Function run when the test times out. 87 * How many payments we want to generate.
475 *
476 * @param cls NULL
477 */ 88 */
478static void 89static unsigned int payments_number = 1;
479do_timeout (void *cls)
480{
481 timeout_task = NULL;
482 GNUNET_SCHEDULER_shutdown ();
483}
484
485 90
486/** 91/**
487 * The generator failed, return with an error code. 92 * How many /tracks operation we want to perform.
488 *
489 * @param is interpreter state to clean up
490 */ 93 */
491static void 94static unsigned int tracks_number = 1;
492fail (struct InterpreterState *is)
493{
494 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
495 "Interpreter failed at step %s (#%u)\n",
496 is->commands[is->ip].label,
497 is->ip);
498 result = GNUNET_SYSERR;
499 GNUNET_SCHEDULER_shutdown ();
500}
501 95
502 96
503/** 97/**
504 * Run the main interpreter loop that performs exchange operations. 98 * Usually set as ~/.config/taler.net
505 *
506 * @param cls contains the `struct InterpreterState`
507 */ 99 */
508static void 100static const char *default_config_file;
509interpreter_run (void *cls);
510
511 101
512/** 102/**
513 * Run the next command with the interpreter. 103 * Log level used during the run.
514 *
515 * @param is current interpeter state.
516 */ 104 */
517static void 105static char *loglev;
518next_command (struct InterpreterState *is)
519{
520 is->ip++;
521 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
522 is);
523}
524
525 106
526/** 107/**
527 * Callback that processes GET /proposal's output. 108 * Config filename.
528 *
529 * @param cls closure
530 * @param http_status HTTP response code, 200 indicates success;
531 * 0 if the backend's reply is bogus (fails to follow the protocol)
532 * @param ec taler-specific error code
533 * @param obj the full received JSON reply, or
534 * error details if the request failed
535 * @param contract_terms the order + additional information provided by the
536 * backend, NULL on error.
537 * @param sig merchant's signature over the contract, NULL on error
538 * @param h_contract hash of the contract, NULL on error
539 */ 109 */
540static void 110static char *cfg_filename;
541proposal_lookup_cb (void *cls,
542 unsigned int http_status,
543 const json_t *obj,
544 const json_t *contract_terms,
545 const struct TALER_MerchantSignatureP *sig,
546 const struct GNUNET_HashCode *hash)
547{
548 struct InterpreterState *is = cls;
549 struct Command *cmd = &is->commands[is->ip];
550
551 cmd->details.proposal.plo = NULL;
552 switch (http_status)
553 {
554 case MHD_HTTP_OK:
555 cmd->details.proposal.contract_terms = json_incref ((json_t *) contract_terms);
556 cmd->details.proposal.merchant_sig = *sig;
557 cmd->details.proposal.hash = *hash;
558 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
559 "Hashed proposal, '%s'\n",
560 GNUNET_h2s (hash));
561 break;
562 default:
563 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
564 "unexpected status code from /proposal: %u. Step %u\n",
565 http_status,
566 is->ip);
567 json_dumpf (obj, stderr, 0);
568 GNUNET_break (0);
569 fail (is);
570 return;
571 }
572 next_command (is);
573}
574 111
575/** 112/**
576 * Callback that works PUT /proposal's output. 113 * Bank base URL.
577 *
578 * @param cls closure
579 * @param http_status HTTP response code, 200 indicates success;
580 * 0 if the backend's reply is bogus (fails to follow the protocol)
581 * @param ec taler-specific error code
582 * @param obj the full received JSON reply, or
583 * error details if the request failed
584 */ 114 */
585static void 115static char *bank_url;
586proposal_cb (void *cls,
587 unsigned int http_status,
588 enum TALER_ErrorCode ec,
589 const json_t *obj,
590 const char *order_id)
591{
592 struct InterpreterState *is = cls;
593 struct Command *cmd = &is->commands[is->ip];
594
595 cmd->details.proposal.po = NULL;
596 switch (http_status)
597 {
598 case MHD_HTTP_OK: {
599 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
600 struct GNUNET_CRYPTO_EddsaPublicKey pub;
601
602 priv = GNUNET_CRYPTO_eddsa_key_create ();
603 GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
604 GNUNET_free (priv);
605
606 cmd->details.proposal.plo
607 = TALER_MERCHANT_proposal_lookup (ctx,
608 merchant_url,
609 order_id,
610 instance,
611 &pub,
612 &proposal_lookup_cb,
613 is);
614 }
615 break;
616 default:
617 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
618 "unexpected status code from /proposal: %u. Step %u\n",
619 http_status,
620 is->ip);
621 json_dumpf (obj, stderr, 0);
622 GNUNET_break (0);
623 fail (is);
624 return;
625 }
626}
627
628 116
629/** 117/**
630 * Function called with the result of a /pay operation. 118 * Log file.
631 *
632 * @param cls closure with the interpreter state
633 * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
634 * 0 if the exchange's reply is bogus (fails to follow the protocol)
635 * @param ec taler-specific error code
636 * @param obj the received JSON reply, should be kept as proof (and, in case of errors,
637 * be forwarded to the customer)
638 */ 119 */
639static void 120static char *logfile;
640pay_cb (void *cls,
641 unsigned int http_status,
642 enum TALER_ErrorCode ec,
643 const json_t *obj)
644{
645 struct InterpreterState *is = cls;
646 struct Command *cmd = &is->commands[is->ip];
647 struct PaymentResponsePS mr;
648 struct GNUNET_CRYPTO_EddsaSignature sig;
649 struct GNUNET_HashCode h_contract_terms;
650 const char *error_name;
651 unsigned int error_line;
652
653 cmd->details.pay.ph = NULL;
654 if (cmd->expected_response_code != http_status)
655 {
656 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
657 "Unexpected response code %u to command %s\n",
658 http_status,
659 cmd->label);
660 json_dumpf (obj, stderr, 0);
661 fail (is);
662 return;
663 }
664 if (MHD_HTTP_OK == http_status)
665 {
666 /* Check signature */
667 struct GNUNET_JSON_Specification spec[] = {
668 GNUNET_JSON_spec_fixed_auto ("sig", &sig),
669 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &h_contract_terms),
670 GNUNET_JSON_spec_end ()
671 };
672 if (GNUNET_OK !=
673 GNUNET_JSON_parse (obj,
674 spec,
675 &error_name,
676 &error_line))
677 {
678 GNUNET_break_op (0);
679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
680 "Parser failed on %s:%u\n",
681 error_name,
682 error_line);
683 fail (is);
684 return;
685 }
686 mr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK);
687 mr.purpose.size = htonl (sizeof (mr));
688 mr.h_contract_terms = h_contract_terms;
689 if (GNUNET_OK !=
690 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_PAYMENT_OK,
691 &mr.purpose,
692 &sig,
693 &cmd->details.pay.merchant_pub.eddsa_pub))
694 {
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 "Merchant signature given in response to /pay invalid\n");
697 fail (is);
698 return;
699 }
700
701 }
702 next_command (is);
703}
704
705 121
706/** 122/**
707 * Function called upon completion of our /admin/add/incoming request. 123 * Merchant base URL.
708 *
709 * @param cls closure with the interpreter state
710 * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
711 * 0 if the exchange's reply is bogus (fails to follow the protocol)
712 * @param ec taler-specific error code, #TALER_EC_NONE on success
713 * @param serial_id unique ID of for the transfer at the bank
714 * @param full_response full response from the exchange (for logging, in case of errors)
715 */ 124 */
716static void 125static char *merchant_url;
717add_incoming_cb (void *cls,
718 unsigned int http_status,
719 enum TALER_ErrorCode ec,
720 uint64_t serial_id,
721 const json_t *full_response)
722{
723 struct InterpreterState *is = cls;
724 struct Command *cmd = &is->commands[is->ip];
725
726 cmd->details.admin_add_incoming.aih = NULL;
727 cmd->details.admin_add_incoming.serial_id = serial_id;
728 if (MHD_HTTP_OK != http_status)
729 {
730 GNUNET_break (0);
731 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
732 "%s",
733 json_dumps (full_response,
734 JSON_INDENT (2)));
735 fail (is);
736 return;
737 }
738 next_command (is);
739}
740
741 126
742/** 127/**
743 * Find a command by label. 128 * Currency used.
744 *
745 * @param is interpreter state to search
746 * @param label label to look for
747 * @return NULL if command was not found
748 */ 129 */
749static const struct Command * 130static char *currency;
750find_command (const struct InterpreterState *is,
751 const char *label)
752{
753 const struct Command *cmd;
754
755 if (NULL == label)
756 {
757 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
758 "Attempt to lookup command for empty label\n");
759 return NULL;
760 }
761 for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
762 if ( (NULL != cmd->label) &&
763 (0 == strcmp (cmd->label,
764 label)) )
765 return cmd;
766 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
767 "Command not found: %s\n",
768 label);
769 return NULL;
770}
771
772 131
773/** 132/**
774 * Function called upon completion of our /reserve/withdraw request. 133 * Convenience macros to allocate all the currency-dependant
775 * 134 * strings; note that the argument list of the macro is ignored.
776 * @param cls closure with the interpreter state 135 * It is kept as a way to make the macro more auto-descriptive
777 * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request 136 * where it is called.
778 * 0 if the exchange's reply is bogus (fails to follow the protocol) 137 */
779 * @param ec taler-specific error code 138
780 * @param sig signature over the coin, NULL on error 139#define ALLOCATE_AMOUNTS(...) \
781 * @param full_response full response from the exchange (for logging, in case of errors) 140 GNUNET_asprintf (&CURRENCY_25_05, \
141 "%s:25.05", \
142 currency); \
143 GNUNET_asprintf (&CURRENCY_10, \
144 "%s:10", \
145 currency); \
146 GNUNET_asprintf (&CURRENCY_9_98, \
147 "%s:9.98", \
148 currency); \
149 GNUNET_asprintf (&CURRENCY_5, \
150 "%s:5", \
151 currency); \
152 GNUNET_asprintf (&CURRENCY_4_99, \
153 "%s:4.99", \
154 currency); \
155 GNUNET_asprintf (&CURRENCY_0_02, \
156 "%s:0.02", \
157 currency); \
158 GNUNET_asprintf (&CURRENCY_0_01, \
159 "%s:0.01", \
160 currency);
161
162#define ALLOCATE_ORDERS(...) \
163 GNUNET_asprintf \
164 (&order_worth_5, \
165 "{\"max_fee\":\
166 {\"currency\":\"%s\",\
167 \"value\":0,\
168 \"fraction\":50000000},\
169 \"refund_deadline\":\"\\/Date(0)\\/\",\
170 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
171 \"amount\":\
172 {\"currency\":\"%s\",\
173 \"value\":5,\
174 \"fraction\":0},\
175 \"summary\": \"merchant-lib testcase\",\
176 \"fulfillment_url\": \"https://example.com/\",\
177 \"products\": [ {\"description\":\"ice cream\",\
178 \"value\":\"{%s:5}\"} ] }", \
179 currency, \
180 currency, \
181 currency); \
182 GNUNET_asprintf \
183 (&order_worth_10_2coins, \
184 "{\"max_fee\":\
185 {\"currency\":\"%s\",\
186 \"value\":0,\
187 \"fraction\":50000000},\
188 \"refund_deadline\":\"\\/Date(0)\\/\",\
189 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
190 \"amount\":\
191 {\"currency\":\"%s\",\
192 \"value\":10,\
193 \"fraction\":0},\
194 \"summary\": \"2-coins untracked payment\",\
195 \"fulfillment_url\": \"https://example.com/\",\
196 \"products\": [ {\"description\":\"2-coins payment\",\
197 \"value\":\"{%s:10}\"} ] }", \
198 currency, \
199 currency, \
200 currency); \
201 GNUNET_asprintf \
202 (&order_worth_5_track, \
203 "{\"max_fee\":\
204 {\"currency\":\"%s\",\
205 \"value\":0,\
206 \"fraction\":50000000},\
207 \"refund_deadline\":\"\\/Date(0)\\/\",\
208 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
209 \"amount\":\
210 {\"currency\":\"%s\",\
211 \"value\":5,\
212 \"fraction\":0},\
213 \"summary\": \"ice track cream!\",\
214 \"fulfillment_url\": \"https://example.com/\",\
215 \"products\": [ {\"description\":\"ice track cream\",\
216 \"value\":\"{%s:5}\"} ] }", \
217 currency, \
218 currency, \
219 currency); \
220 GNUNET_asprintf \
221 (&order_worth_5_unaggregated, \
222 "{\"max_fee\":\
223 {\"currency\":\"%s\",\
224 \"value\":0,\
225 \"fraction\":50000000},\
226 \"refund_deadline\":\"\\/Date(0)\\/\",\
227 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
228 \"amount\":\
229 {\"currency\":\"%s\",\
230 \"value\":5,\
231 \"fraction\":0},\
232 \"summary\": \"unaggregated deposit!\",\
233 \"fulfillment_url\": \"https://example.com/\",\
234 \"products\": [ {\"description\":\"unaggregated cream\",\
235 \"value\":\"{%s:5}\"} ] }", \
236 currency, \
237 currency, \
238 currency);
239
240/**
241 * Actual commands collection.
782 */ 242 */
783static void 243static void
784reserve_withdraw_cb (void *cls, 244run (void *cls,
785 unsigned int http_status, 245 struct TALER_TESTING_Interpreter *is)
786 enum TALER_ErrorCode ec,
787 const struct TALER_DenominationSignature *sig,
788 const json_t *full_response)
789{ 246{
790 struct InterpreterState *is = cls;
791 struct Command *cmd = &is->commands[is->ip];
792 247
793 cmd->details.reserve_withdraw.wsh = NULL; 248 /* Currency strings. */
794 if (cmd->expected_response_code != http_status) 249 char *CURRENCY_25_05;
795 { 250 char *CURRENCY_10;
796 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 251 char *CURRENCY_9_98;
797 "Unexpected response code %u to command %s\n", 252 char *CURRENCY_5;
798 http_status, 253 char *CURRENCY_4_99;
799 cmd->label); 254 char *CURRENCY_0_02;
800 json_dumpf (full_response, stderr, 0); 255 char *CURRENCY_0_01;
801 GNUNET_break (0); 256
802 fail (is); 257 ALLOCATE_AMOUNTS
803 return; 258 (CURRENCY_25_05,
804 } 259 CURRENCY_10,
805 switch (http_status) 260 CURRENCY_9_98,
806 { 261 CURRENCY_5,
807 case MHD_HTTP_OK: 262 CURRENCY_4_99,
808 if (NULL == sig) 263 CURRENCY_0_02,
809 { 264 CURRENCY_0_01);
810 GNUNET_break (0); 265
811 fail (is); 266
812 return; 267 /* Orders. */
813 } 268 char *order_worth_5;
814 cmd->details.reserve_withdraw.sig.rsa_signature 269 char *order_worth_10_2coins;
815 = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); 270 char *order_worth_5_track;
816 break; 271 char *order_worth_5_unaggregated;
817 case MHD_HTTP_PAYMENT_REQUIRED: 272
818 /* nothing to check */ 273 ALLOCATE_ORDERS
819 break; 274 (order_worth_5,
820 default: 275 order_worth_10_2coins,
821 /* Unsupported status code (by test harness) */ 276 order_worth_5_track,
822 GNUNET_break (0); 277 order_worth_5_unaggregated);
823 break; 278
824 } 279 struct TALER_TESTING_Command commands[] = {
825 next_command (is); 280
826} 281 CMD_TRANSFER_TO_EXCHANGE
827 282 ("create-reserve-1",
828 283 CURRENCY_25_05),
829/** 284
830 * Find denomination key matching the given amount. 285 TALER_TESTING_cmd_exec_wirewatch
831 * 286 ("wirewatch-1",
832 * @param keys array of keys to search 287 cfg_filename),
833 * @param amount coin value to look for 288
834 * @return NULL if no matching key was found 289 TALER_TESTING_cmd_withdraw_amount
835 */ 290 ("withdraw-coin-1",
836static const struct TALER_EXCHANGE_DenomPublicKey * 291 is->exchange, // picks port from config's [exchange].
837find_pk (const struct TALER_EXCHANGE_Keys *keys, 292 "create-reserve-1",
838 const struct TALER_Amount *amount) 293 CURRENCY_5,
839{ 294 MHD_HTTP_OK),
840 struct GNUNET_TIME_Absolute now; 295
841 struct TALER_EXCHANGE_DenomPublicKey *pk; 296 TALER_TESTING_cmd_withdraw_amount
842 char *str; 297 ("withdraw-coin-2",
298 is->exchange,
299 "create-reserve-1",
300 CURRENCY_5,
301 MHD_HTTP_OK),
302
303 /* This coin will be spent but never aggregated,
304 * in order to get 202 responses from tracks. */
305 TALER_TESTING_cmd_withdraw_amount
306 ("withdraw-coin-3",
307 is->exchange,
308 "create-reserve-1",
309 CURRENCY_5,
310 MHD_HTTP_OK),
311
312 /* coin 4 & 5 will be deposited for the same
313 * contract; needed in case some testing utility
314 * wants to trigger a "failed dependency" error. */
315 TALER_TESTING_cmd_withdraw_amount
316 ("withdraw-coin-4",
317 is->exchange,
318 "create-reserve-1",
319 CURRENCY_5,
320 MHD_HTTP_OK),
321
322 TALER_TESTING_cmd_withdraw_amount
323 ("withdraw-coin-5",
324 is->exchange,
325 "create-reserve-1",
326 CURRENCY_5,
327 MHD_HTTP_OK),
328
329 TALER_TESTING_cmd_proposal
330 ("create-proposal-1",
331 merchant_url,
332 is->ctx,
333 MHD_HTTP_OK,
334 order_worth_5,
335 NULL),
336
337 TALER_TESTING_cmd_pay
338 ("deposit-simple",
339 merchant_url,
340 is->ctx,
341 MHD_HTTP_OK,
342 "create-proposal-1",
343 "withdraw-coin-1",
344 CURRENCY_5,
345 CURRENCY_4_99,
346 CURRENCY_0_01),
347
348 TALER_TESTING_cmd_rewind_ip
349 ("rewind-payments",
350 FIRST_INSTRUCTION,
351 &payments_number),
352
353 /* Next proposal-pay cycle will be used by /track CMDs
354 * and so it will not have to be looped over, only /track
355 * CMDs will have to. */
356
357 TALER_TESTING_cmd_proposal
358 ("create-proposal-2",
359 merchant_url,
360 is->ctx,
361 MHD_HTTP_OK,
362 order_worth_5_track,
363 NULL),
364
365 TALER_TESTING_cmd_pay
366 ("deposit-simple-2",
367 merchant_url,
368 is->ctx,
369 MHD_HTTP_OK,
370 "create-proposal-2",
371 "withdraw-coin-2",
372 CURRENCY_5,
373 CURRENCY_4_99,
374 CURRENCY_0_01),
375
376 /* /track/transaction over deposit-simple-2 */
377
378 TALER_TESTING_cmd_exec_aggregator
379 ("aggregate-1",
380 cfg_filename),
381
382 TALER_TESTING_cmd_merchant_track_transaction
383 ("track-transaction-1",
384 merchant_url,
385 is->ctx,
386 MHD_HTTP_OK,
387 "dummy", // "check bank" CMD, never used, to be deleted.
388 "deposit-simple-2",
389 CURRENCY_0_01),
390
391 TALER_TESTING_cmd_merchant_track_transfer
392 ("track-transfer-1",
393 merchant_url,
394 is->ctx,
395 MHD_HTTP_OK,
396 "track-transaction-1",
397 "deposit-simple-2"),
398
399 /* Doing the 2-coins payment; needed to generate the
400 * "failed dependency" response error, at /track/transaction.
401 * NOTE: not used here, but done just in case a testing
402 * program would need it. And this MUST happen here, as
403 * no tracking operation happens next and so the merchant
404 * won't be able to use a cached version in its database
405 * when serving /track/..; therefore it will relate to the
406 * exchange that can be twisted by the testing logic. */
407 TALER_TESTING_cmd_proposal
408 ("create-proposal-4&5",
409 merchant_url,
410 is->ctx,
411 MHD_HTTP_OK,
412 order_worth_10_2coins,
413 NULL),
414
415 TALER_TESTING_cmd_pay ("deposit-4&5",
416 merchant_url,
417 is->ctx,
418 MHD_HTTP_OK,
419 "create-proposal-4&5",
420 "withdraw-coin-4;" \
421 "withdraw-coin-5",
422 CURRENCY_10,
423 CURRENCY_9_98, // no sense now
424 CURRENCY_0_02), // no sense now
425
426 TALER_TESTING_cmd_exec_aggregator
427 ("aggregate-2",
428 cfg_filename),
429
430 /* Must be _after_ any aggregation takes place. */
431 TALER_TESTING_cmd_proposal
432 ("create-proposal-3",
433 merchant_url,
434 is->ctx,
435 MHD_HTTP_OK,
436 order_worth_5_unaggregated,
437 NULL),
438
439 TALER_TESTING_cmd_pay
440 ("deposit-simple-3",
441 merchant_url,
442 is->ctx,
443 MHD_HTTP_OK,
444 "create-proposal-3",
445 "withdraw-coin-3",
446 CURRENCY_5,
447 CURRENCY_4_99,
448 CURRENCY_0_01),
449
450 TALER_TESTING_cmd_merchant_track_transaction
451 ("track-transaction-2",
452 merchant_url,
453 is->ctx,
454 MHD_HTTP_ACCEPTED,
455 "dummy", // "check bank" CMD, never used, to be deleted.
456 "deposit-simple-3",
457 CURRENCY_0_01),
458
459 TALER_TESTING_cmd_rewind_ip
460 ("rewind-tracks",
461 TRACKS_INSTRUCTION,
462 &tracks_number),
463
464 TALER_TESTING_cmd_end ()
465 };
843 466
844 now = GNUNET_TIME_absolute_get (); 467 TALER_TESTING_run (is,
845 for (unsigned int i=0;i<keys->num_denom_keys;i++) 468 commands);
846 {
847 pk = &keys->denom_keys[i];
848 if ( (0 == TALER_amount_cmp (amount,
849 &pk->value)) &&
850 (now.abs_value_us >= pk->valid_from.abs_value_us) &&
851 (now.abs_value_us < pk->withdraw_valid_until.abs_value_us) )
852 return pk;
853 }
854 /* do 2nd pass to check if expiration times are to blame for failure */
855 str = TALER_amount_to_string (amount);
856 for (unsigned int i=0;i<keys->num_denom_keys;i++)
857 {
858 pk = &keys->denom_keys[i];
859 if ( (0 == TALER_amount_cmp (amount,
860 &pk->value)) &&
861 ( (now.abs_value_us < pk->valid_from.abs_value_us) ||
862 (now.abs_value_us > pk->withdraw_valid_until.abs_value_us) ) )
863 {
864 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
865 "Have denomination key for `%s', but with wrong expiration range %llu vs [%llu,%llu)\n",
866 str,
867 (unsigned long long) now.abs_value_us,
868 (unsigned long long) pk->valid_from.abs_value_us,
869 (unsigned long long) pk->withdraw_valid_until.abs_value_us);
870 GNUNET_free (str);
871 return NULL;
872 }
873 }
874 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
875 "No denomination key for amount %s found\n",
876 str);
877 GNUNET_free (str);
878 return NULL;
879} 469}
880 470
881
882/** 471/**
883 * Allocates and return a string representing a order. 472 * Send SIGTERM and wait for process termination.
884 * In this process, this function gives the order those
885 * prices specified by the user. Please NOTE that any amount
886 * must be given in the form "CUR:XX.YY".
887 * 473 *
888 * @param max_fee merchant's allowed max_fee 474 * @param process process to terminate.
889 * @param amount total amount for this order
890 * @return JSON string for the order, NULL on errors
891 */ 475 */
892static json_t * 476void
893make_order (const char *maxfee, 477terminate_process (struct GNUNET_OS_Process *process)
894 const char *total)
895{ 478{
896 struct TALER_Amount tmp_amount; 479 GNUNET_OS_process_kill (process, SIGTERM);
897 json_t *total_j; 480 GNUNET_OS_process_wait (process);
898 json_t *maxfee_j; 481 GNUNET_OS_process_destroy (process);
899 json_t *ret;
900 unsigned long long id;
901 struct GNUNET_TIME_Absolute now;
902 char *timestamp;
903
904 if (GNUNET_OK !=
905 TALER_string_to_amount (maxfee,
906 &tmp_amount))
907 {
908 GNUNET_break (0);
909 return NULL;
910 }
911 maxfee_j = TALER_JSON_from_amount (&tmp_amount);
912 GNUNET_assert (NULL != maxfee_j);
913 if (GNUNET_OK !=
914 TALER_string_to_amount (total,
915 &tmp_amount))
916 {
917 GNUNET_break (0);
918 return NULL;
919 }
920 total_j = TALER_JSON_from_amount (&tmp_amount);
921 GNUNET_assert (NULL != total_j);
922 now = GNUNET_TIME_absolute_get ();
923 GNUNET_TIME_round_abs (&now);
924 GNUNET_asprintf (&timestamp,
925 "/Date(%u)/",
926 now.abs_value_us / 1000LL / 1000LL);
927
928 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
929 &id,
930 sizeof (id));
931 ret = json_pack ("{s:o, s:s, s:s, s:s, s:s, s:o, s:s, s:[{s:s}]}",
932 "max_fee", maxfee_j,
933 "order_id", TALER_b2s (&id, sizeof (id)),
934 "timestamp", timestamp,
935 "refund_deadline", "/Date(0)/",
936 "pay_deadline", "/Date(9999999999)/",
937 "amount", total_j,
938 "summary", "payments generator..",
939 "products", "description", "ice cream");
940
941 GNUNET_assert (NULL != ret);
942 return ret;
943} 482}
944 483
945
946/** 484/**
947 * Free amount strings in interpreter state. 485 * The main function of the serve tool
948 * 486 *
949 * @param is state to reset 487 * @param argc number of arguments from the command line
488 * @param argv command line arguments
489 * @return 0 ok, or `enum PaymentGeneratorError` on error
950 */ 490 */
951static void 491int
952free_interpreter_amounts (struct InterpreterState *is) 492main (int argc,
493 char *const *argv)
953{ 494{
954 struct Command *cmd; 495 struct GNUNET_CONFIGURATION_Handle *cfg;
955
956 for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
957 switch (cmd->oc)
958 {
959 case OC_END:
960 GNUNET_assert (0);
961 break;
962 case OC_PAY:
963 GNUNET_free (cmd->details.pay.amount_with_fee);
964 GNUNET_free (cmd->details.pay.amount_without_fee);
965 break;
966 case OC_PROPOSAL:
967 GNUNET_free (cmd->details.proposal.max_fee);
968 GNUNET_free (cmd->details.proposal.amount);
969 break;
970 case OC_WITHDRAW_SIGN:
971 GNUNET_free (cmd->details.reserve_withdraw.amount);
972 break;
973 case OC_ADMIN_ADD_INCOMING:
974 GNUNET_free (cmd->details.admin_add_incoming.amount);
975 break;
976 case OC_RUN_WIREWATCH:
977 break;
978 default:
979 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
980 "Shutdown: unknown instruction %d at %u (%s)\n",
981 cmd->oc,
982 i,
983 cmd->label);
984 break;
985 }
986}
987 496
497 default_config_file = GNUNET_OS_project_data_get
498 ()->user_config_file;
988 499
989/** 500 struct GNUNET_GETOPT_CommandLineOption options[] = {
990 * Reset the interpreter state.
991 *
992 * @param is state to reset
993 */
994static void
995reset_interpreter (struct InterpreterState *is)
996{
997 struct Command *cmd;
998
999 for (unsigned int i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
1000 switch (cmd->oc)
1001 {
1002 case OC_END:
1003 GNUNET_assert (0);
1004 break;
1005
1006 case OC_PAY:
1007 if (NULL != cmd->details.pay.ph)
1008 {
1009 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1010 "Command %u (%s) did not complete\n",
1011 i,
1012 cmd->label);
1013 TALER_MERCHANT_pay_cancel (cmd->details.pay.ph);
1014 cmd->details.pay.ph = NULL;
1015 }
1016 break;
1017
1018 case OC_PROPOSAL:
1019 if (NULL != cmd->details.proposal.po)
1020 {
1021 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1022 "Command %u (%s) did not complete\n",
1023 i,
1024 cmd->label);
1025 TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po);
1026 cmd->details.proposal.po = NULL;
1027 }
1028 if (NULL != cmd->details.proposal.contract_terms)
1029 {
1030 json_decref (cmd->details.proposal.contract_terms);
1031 cmd->details.proposal.contract_terms = NULL;
1032 }
1033 break;
1034
1035 case OC_WITHDRAW_SIGN:
1036 if (NULL != cmd->details.reserve_withdraw.wsh)
1037 {
1038 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1039 "Command %u (%s) did not complete\n",
1040 i,
1041 cmd->label);
1042 TALER_EXCHANGE_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh);
1043 cmd->details.reserve_withdraw.wsh = NULL;
1044 }
1045 if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature)
1046 {
1047 GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature);
1048 cmd->details.reserve_withdraw.sig.rsa_signature = NULL;
1049 }
1050 break;
1051
1052 case OC_ADMIN_ADD_INCOMING:
1053 if (NULL != cmd->details.admin_add_incoming.aih)
1054 {
1055 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1056 "Command %u (%s) did not complete\n",
1057 i,
1058 cmd->label);
1059 TALER_BANK_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih);
1060 cmd->details.admin_add_incoming.aih = NULL;
1061 }
1062 break;
1063 case OC_RUN_WIREWATCH:
1064 if (NULL != cmd->details.run_wirewatch.wirewatch_proc)
1065 {
1066 GNUNET_break (0 ==
1067 GNUNET_OS_process_kill (cmd->details.run_wirewatch.wirewatch_proc,
1068 SIGKILL));
1069 GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
1070 GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
1071 cmd->details.run_wirewatch.wirewatch_proc = NULL;
1072 }
1073 if (NULL != cmd->details.run_wirewatch.child_death_task)
1074 {
1075 GNUNET_SCHEDULER_cancel (cmd->details.run_wirewatch.child_death_task);
1076 cmd->details.run_wirewatch.child_death_task = NULL;
1077 }
1078 break;
1079 default:
1080 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1081 "Shutdown: unknown instruction %d at %u (%s)\n",
1082 cmd->oc,
1083 i,
1084 cmd->label);
1085 break;
1086 }
1087}
1088
1089 501
1090/** 502 GNUNET_GETOPT_option_cfgfile
1091 * Task triggered whenever we receive a SIGCHLD (child 503 (&cfg_filename),
1092 * process died).
1093 *
1094 * @param cls closure, NULL if we need to self-restart
1095 */
1096static void
1097maint_child_death (void *cls)
1098{
1099 struct InterpreterState *is = cls;
1100 struct Command *cmd = &is->commands[is->ip];
1101 const struct GNUNET_DISK_FileHandle *pr;
1102 char c[16];
1103
1104 cmd->details.run_wirewatch.child_death_task = NULL;
1105 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
1106 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
1107 GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
1108 GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
1109 cmd->details.run_wirewatch.wirewatch_proc = NULL;
1110 next_command (is);
1111}
1112 504
505 GNUNET_GETOPT_option_version
506 (PACKAGE_VERSION " " VCS_VERSION),
1113 507
1114/** 508 GNUNET_GETOPT_option_help
1115 * Run the main interpreter loop that performs exchange operations. 509 ("Generate Taler payments to populate the database(s)"),
1116 *
1117 * @param cls contains the `struct InterpreterState`
1118 */
1119static void
1120interpreter_run (void *cls)
1121{
1122 const struct GNUNET_SCHEDULER_TaskContext *tc;
1123 struct InterpreterState *is = cls;
1124 struct Command *cmd = &is->commands[is->ip];
1125 const struct Command *ref;
1126 struct TALER_Amount amount;
1127
1128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129 "Interpreter runs command %u/%s(%u)\n",
1130 is->ip,
1131 cmd->label,
1132 cmd->oc);
1133
1134 is->task = NULL;
1135 tc = GNUNET_SCHEDULER_get_task_context ();
1136 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1137 {
1138 fprintf (stderr,
1139 "Test aborted by shutdown request\n");
1140 fail (is);
1141 return;
1142 }
1143 510
1144 switch (cmd->oc) 511 GNUNET_GETOPT_option_loglevel
1145 { 512 (&loglev),
1146 case OC_END:
1147 j++;
1148 if (j < times)
1149 {
1150 reset_interpreter (is);
1151 is->ip = 0;
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "Rewinding the interpreter.\n");
1154 GNUNET_SCHEDULER_add_now (&interpreter_run,
1155 is);
1156 return;
1157 }
1158 result = GNUNET_OK;
1159 GNUNET_SCHEDULER_shutdown ();
1160 return;
1161 case OC_PAY:
1162 {
1163 struct TALER_MERCHANT_PayCoin pc;
1164 const char *order_id;
1165 struct GNUNET_TIME_Absolute refund_deadline;
1166 struct GNUNET_TIME_Absolute pay_deadline;
1167 struct GNUNET_TIME_Absolute timestamp;
1168 struct GNUNET_HashCode h_wire;
1169 struct TALER_MerchantPublicKeyP merchant_pub;
1170 struct TALER_MerchantSignatureP merchant_sig;
1171 struct TALER_Amount total_amount;
1172 struct TALER_Amount max_fee;
1173 const char *error_name;
1174 unsigned int error_line;
1175
1176 /* get proposal */
1177 ref = find_command (is,
1178 cmd->details.pay.contract_ref);
1179 GNUNET_assert (NULL != ref);
1180 merchant_sig = ref->details.proposal.merchant_sig;
1181 GNUNET_assert (NULL != ref->details.proposal.contract_terms);
1182 {
1183 /* Get information that need to be replied in the deposit permission */
1184 struct GNUNET_JSON_Specification spec[] = {
1185 GNUNET_JSON_spec_string ("order_id", &order_id),
1186 GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline),
1187 GNUNET_JSON_spec_absolute_time ("pay_deadline", &pay_deadline),
1188 GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
1189 GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
1190 GNUNET_JSON_spec_fixed_auto ("H_wire", &h_wire),
1191 TALER_JSON_spec_amount ("amount", &total_amount),
1192 TALER_JSON_spec_amount ("max_fee", &max_fee),
1193 GNUNET_JSON_spec_end()
1194 };
1195
1196 if (GNUNET_OK !=
1197 GNUNET_JSON_parse (ref->details.proposal.contract_terms,
1198 spec,
1199 &error_name,
1200 &error_line))
1201 {
1202 GNUNET_break_op (0);
1203 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1204 "Parser failed on %s:%u\n",
1205 error_name,
1206 error_line);
1207 fail (is);
1208 return;
1209 }
1210 cmd->details.pay.merchant_pub = merchant_pub;
1211 }
1212
1213 {
1214 const struct Command *coin_ref;
1215
1216 memset (&pc, 0, sizeof (pc));
1217 coin_ref = find_command (is,
1218 cmd->details.pay.coin_ref);
1219 GNUNET_assert (NULL != coin_ref);
1220 switch (coin_ref->oc)
1221 {
1222 case OC_WITHDRAW_SIGN:
1223 pc.coin_priv = coin_ref->details.reserve_withdraw.ps.coin_priv;
1224 pc.denom_pub = coin_ref->details.reserve_withdraw.pk->key;
1225 pc.denom_sig = coin_ref->details.reserve_withdraw.sig;
1226 pc.denom_value = coin_ref->details.reserve_withdraw.pk->value;
1227 pc.exchange_url = exchange_url;
1228 break;
1229 default:
1230 GNUNET_assert (0);
1231 }
1232
1233 if (GNUNET_OK !=
1234 TALER_string_to_amount (cmd->details.pay.amount_without_fee,
1235 &pc.amount_without_fee))
1236 {
1237 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1238 "Failed to parse amount `%s' at %u\n",
1239 cmd->details.pay.amount_without_fee,
1240 is->ip);
1241 fail (is);
1242 return;
1243 }
1244
1245 if (GNUNET_OK !=
1246 TALER_string_to_amount (cmd->details.pay.amount_with_fee,
1247 &pc.amount_with_fee))
1248 {
1249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1250 "Failed to parse amount `%s' at %u\n",
1251 cmd->details.pay.amount_with_fee,
1252 is->ip);
1253 fail (is);
1254 return;
1255 }
1256 }
1257 cmd->details.pay.ph
1258 = TALER_MERCHANT_pay_wallet (ctx,
1259 merchant_url,
1260 instance,
1261 &ref->details.proposal.hash,
1262 &total_amount,
1263 &max_fee,
1264 &merchant_pub,
1265 &merchant_sig,
1266 timestamp,
1267 refund_deadline,
1268 pay_deadline,
1269 &h_wire,
1270 order_id,
1271 1 /* num_coins */,
1272 &pc /* coins */,
1273 &pay_cb,
1274 is);
1275 }
1276 if (NULL == cmd->details.pay.ph)
1277 {
1278 GNUNET_break (0);
1279 fail (is);
1280 return;
1281 }
1282 return;
1283 case OC_PROPOSAL:
1284 {
1285 json_t *order;
1286 json_t *merchant_obj;
1287
1288 order = make_order (cmd->details.proposal.max_fee,
1289 cmd->details.proposal.amount);
1290
1291
1292 if (NULL == order)
1293 {
1294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1295 "Failed to create the order at command #%u\n",
1296 is->ip);
1297 fail (is);
1298 return;
1299 }
1300
1301 GNUNET_assert (NULL != (merchant_obj = json_pack ("{s:{s:s}}",
1302 "merchant",
1303 "instance",
1304 instance)));
1305
1306
1307 GNUNET_assert (-1 != json_object_update (order,
1308 merchant_obj));
1309 json_decref (merchant_obj);
1310 cmd->details.proposal.po
1311 = TALER_MERCHANT_order_put (ctx,
1312 merchant_url,
1313 order,
1314 &proposal_cb,
1315 is);
1316 json_decref (order);
1317 if (NULL == cmd->details.proposal.po)
1318 {
1319 GNUNET_break (0);
1320 fail (is);
1321 return;
1322 }
1323 return;
1324 }
1325
1326 case OC_ADMIN_ADD_INCOMING:
1327 {
1328 char *subject;
1329 struct TALER_BANK_AuthenticationData auth;
1330 struct TALER_ReservePublicKeyP reserve_pub;
1331
1332 if (NULL !=
1333 cmd->details.admin_add_incoming.reserve_reference)
1334 {
1335 ref = find_command (is,
1336 cmd->details.admin_add_incoming.reserve_reference);
1337 GNUNET_assert (NULL != ref);
1338 GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
1339 cmd->details.admin_add_incoming.reserve_priv
1340 = ref->details.admin_add_incoming.reserve_priv;
1341 }
1342 else
1343 {
1344 struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
1345
1346 priv = GNUNET_CRYPTO_eddsa_key_create ();
1347 cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
1348 GNUNET_free (priv);
1349 }
1350 GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
1351 &reserve_pub.eddsa_pub);
1352 if (GNUNET_OK !=
1353 TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
1354 &amount))
1355 {
1356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1357 "Failed to parse amount `%s' at %u\n",
1358 cmd->details.admin_add_incoming.amount,
1359 is->ip);
1360 fail (is);
1361 return;
1362 }
1363
1364 subject
1365 = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
1366 sizeof (reserve_pub));
1367 auth.method = TALER_BANK_AUTH_BASIC;
1368 /* TODO: obtain authentication details from configuration */
1369 auth.details.basic.username = "admin";
1370 auth.details.basic.password = "x";
1371 cmd->details.admin_add_incoming.aih
1372 = TALER_BANK_admin_add_incoming (ctx,
1373 bank_url,
1374 &auth,
1375 exchange_url,
1376 subject,
1377 &amount,
1378 cmd->details.admin_add_incoming.debit_account_no,
1379 cmd->details.admin_add_incoming.credit_account_no,
1380 &add_incoming_cb,
1381 is);
1382 GNUNET_free (subject);
1383 if (NULL == cmd->details.admin_add_incoming.aih)
1384 {
1385 GNUNET_break (0);
1386 fail (is);
1387 return;
1388 }
1389 return;
1390 }
1391 case OC_RUN_WIREWATCH:
1392 {
1393 const struct GNUNET_DISK_FileHandle *pr;
1394
1395 cmd->details.run_wirewatch.wirewatch_proc
1396 = GNUNET_OS_start_process (GNUNET_NO,
1397 GNUNET_OS_INHERIT_STD_ALL,
1398 NULL, NULL, NULL,
1399 "taler-exchange-wirewatch",
1400 "taler-exchange-wirewatch",
1401 "-c", cfgfilename,
1402 "-T", /* exit when done */
1403 NULL);
1404 if (NULL == cmd->details.run_wirewatch.wirewatch_proc)
1405 {
1406 GNUNET_break (0);
1407 fail (is);
1408 return;
1409 }
1410 pr = GNUNET_DISK_pipe_handle (sigpipe,
1411 GNUNET_DISK_PIPE_END_READ);
1412 cmd->details.run_wirewatch.child_death_task
1413 = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1414 pr,
1415 &maint_child_death,
1416 is);
1417 return;
1418 }
1419 case OC_WITHDRAW_SIGN:
1420 GNUNET_assert (NULL !=
1421 cmd->details.reserve_withdraw.reserve_reference);
1422 ref = find_command (is,
1423 cmd->details.reserve_withdraw.reserve_reference);
1424 GNUNET_assert (NULL != ref);
1425 GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
1426 if (NULL != cmd->details.reserve_withdraw.amount)
1427 {
1428 if (GNUNET_OK !=
1429 TALER_string_to_amount (cmd->details.reserve_withdraw.amount,
1430 &amount))
1431 {
1432 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1433 "Failed to parse amount `%s' at %u\n",
1434 cmd->details.reserve_withdraw.amount,
1435 is->ip);
1436 fail (is);
1437 return;
1438 }
1439 cmd->details.reserve_withdraw.pk = find_pk (is->keys,
1440 &amount);
1441 }
1442 if (NULL == cmd->details.reserve_withdraw.pk)
1443 {
1444 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1445 "Failed to determine denomination key at %u\n",
1446 is->ip);
1447 fail (is);
1448 return;
1449 }
1450
1451 TALER_planchet_setup_random (&cmd->details.reserve_withdraw.ps);
1452 cmd->details.reserve_withdraw.wsh
1453 = TALER_EXCHANGE_reserve_withdraw (exchange,
1454 cmd->details.reserve_withdraw.pk,
1455 &ref->details.admin_add_incoming.reserve_priv,
1456 &cmd->details.reserve_withdraw.ps,
1457 &reserve_withdraw_cb,
1458 is);
1459 if (NULL == cmd->details.reserve_withdraw.wsh)
1460 {
1461 GNUNET_break (0);
1462 fail (is);
1463 return;
1464 }
1465 return;
1466
1467 default:
1468 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1469 "Unknown command, OC: %d, label: %s.\n",
1470 cmd->oc,
1471 cmd->label);
1472 fail (is);
1473 }
1474}
1475 513
514 GNUNET_GETOPT_option_uint
515 ('p',
516 "payments-number",
517 "PN",
518 "will generate PN payments, defaults to 1",
519 &payments_number),
1476 520
1477/** 521 GNUNET_GETOPT_option_uint
1478 * Functions of this type are called to provide the retrieved signing and 522 ('t',
1479 * denomination keys of the exchange. No TALER_EXCHANGE_*() functions should 523 "tracks-number",
1480 * be called in this callback. 524 "TN",
1481 * 525 "will perform TN /track operations, defaults to 1",
1482 * @param cls closure 526 &tracks_number),
1483 * @param keys information about keys of the exchange
1484 * @param compat version compatibility data
1485 */
1486static void
1487cert_cb (void *cls,
1488 const struct TALER_EXCHANGE_Keys *keys,
1489 enum TALER_EXCHANGE_VersionCompatibility compat)
1490{
1491 struct InterpreterState *is = cls;
1492
1493 /* check that keys is OK */
1494#define ERR(cond) do { if(!(cond)) break; GNUNET_break (0); GNUNET_SCHEDULER_shutdown(); return; } while (0)
1495 ERR (NULL == keys);
1496 ERR (0 == keys->num_sign_keys);
1497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1498 "Read %u signing keys\n",
1499 keys->num_sign_keys);
1500 ERR (0 == keys->num_denom_keys);
1501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1502 "Read %u denomination keys\n",
1503 keys->num_denom_keys);
1504#undef ERR
1505
1506 /* run actual tests via interpreter-loop */
1507 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1508 "Certificate callback invoked, starting interpreter\n");
1509 is->keys = keys;
1510
1511 is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
1512 is);
1513}
1514
1515/**
1516 * Signal handler called for SIGCHLD. Triggers the
1517 * respective handler by writing to the trigger pipe.
1518 */
1519static void
1520sighandler_child_death ()
1521{
1522 static char c;
1523 int old_errno = errno; /* back-up errno */
1524
1525 GNUNET_break (1 ==
1526 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
1527 (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
1528 &c, sizeof (c)));
1529 errno = old_errno; /* restore errno */
1530}
1531 527
528 /**
529 * NOTE: useful when the setup serves merchant
530 * backends via unix domain sockets, since there
531 * is no way - yet? - to get the merchant base url.
532 * Clearly, we could introduce a merchant_base_url
533 * value into the configuration.
534 */
535 GNUNET_GETOPT_option_string
536 ('m',
537 "merchant-url",
538 "MU",
539 "merchant base url, mandatory",
540 &merchant_url),
541
542 GNUNET_GETOPT_option_string
543 ('b',
544 "bank-url",
545 "BU",
546 "bank base url, mandatory",
547 &bank_url),
548
549 GNUNET_GETOPT_option_string
550 ('l',
551 "logfile",
552 "LF",
553 "will log to file LF",
554 &logfile),
1532 555
1533/** 556 GNUNET_GETOPT_OPTION_END
1534 * Function run when the test terminates (good or bad). 557 };
1535 * Cleans up our state.
1536 *
1537 * @param cls the interpreter state.
1538 */
1539static void
1540do_shutdown (void *cls)
1541{
1542 struct InterpreterState *is = cls;
1543 558
1544 if (NULL != timeout_task) 559 result = GNUNET_GETOPT_run
1545 { 560 ("taler-merchant-generate-payments-new",
1546 GNUNET_SCHEDULER_cancel (timeout_task); 561 options,
1547 timeout_task = NULL; 562 argc,
1548 } 563 argv);
1549 564
1550 reset_interpreter (is); 565 if (GNUNET_NO == result)
1551 if (NULL != is->task)
1552 {
1553 GNUNET_SCHEDULER_cancel (is->task);
1554 is->task = NULL;
1555 }
1556 free_interpreter_amounts (is);
1557 GNUNET_free (is->commands);
1558 GNUNET_free (is);
1559 if (NULL != exchange)
1560 {
1561 TALER_EXCHANGE_disconnect (exchange);
1562 exchange = NULL;
1563 }
1564 if (NULL != ctx)
1565 {
1566 GNUNET_CURL_fini (ctx);
1567 ctx = NULL;
1568 }
1569 if (NULL != rc)
1570 {
1571 GNUNET_CURL_gnunet_rc_destroy (rc);
1572 rc = NULL;
1573 }
1574 if (NULL != fakebank)
1575 { 566 {
1576 TALER_FAKEBANK_stop (fakebank); 567 /* --help or --version were given, just return. */
1577 fakebank = NULL; 568 return 0;
1578 } 569 }
1579}
1580
1581
1582/**
1583 * Take currency and the part after ":" in the
1584 * "CURRENCY:XX.YY" format, and return a string
1585 * in the format "CURRENCY:XX.YY".
1586 *
1587 * @param currency currency
1588 * @param rpart float numbers after the ":", in string form
1589 * @return pointer to allocated and concatenated "CURRENCY:XX.YY"
1590 * formatted string.
1591 */
1592static char *
1593concat_amount (const char *currency,
1594 const char *rpart)
1595{
1596 char *str;
1597
1598 GNUNET_asprintf (&str,
1599 "%s:%s",
1600 currency,
1601 rpart);
1602 return str;
1603}
1604
1605
1606/**
1607 * Actually runs the test.
1608 */
1609static void
1610run_test ()
1611{
1612 struct InterpreterState *is;
1613 struct Command commands[] =
1614 {
1615 /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
1616 { .oc = OC_ADMIN_ADD_INCOMING,
1617 .label = "create-reserve-1",
1618 .expected_response_code = MHD_HTTP_OK,
1619 .details.admin_add_incoming.debit_account_no = 62,
1620 .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
1621 .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
1622
1623 /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
1624 { .oc = OC_ADMIN_ADD_INCOMING,
1625 .label = "create-reserve-2",
1626 .expected_response_code = MHD_HTTP_OK,
1627 .details.admin_add_incoming.debit_account_no = 62,
1628 .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
1629 .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
1630 /* Fill reserve with EUR:5.01, as withdraw fee is 1 ct per config */
1631 { .oc = OC_ADMIN_ADD_INCOMING,
1632 .label = "create-reserve-3",
1633 .expected_response_code = MHD_HTTP_OK,
1634 .details.admin_add_incoming.debit_account_no = 62,
1635 .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
1636 .details.admin_add_incoming.amount = concat_amount (currency, "5.01") },
1637 { .oc = OC_RUN_WIREWATCH,
1638 .label = "run-wirewatch-1"
1639 },
1640 /* Withdraw a 5 EUR coin, at fee of 1 ct */
1641 { .oc = OC_WITHDRAW_SIGN,
1642 .label = "withdraw-coin-1",
1643 .expected_response_code = MHD_HTTP_OK,
1644 .details.reserve_withdraw.reserve_reference = "create-reserve-1",
1645 .details.reserve_withdraw.amount = concat_amount (currency, "5") },
1646
1647 /* Withdraw a 5 EUR coin, at fee of 1 ct */
1648 { .oc = OC_WITHDRAW_SIGN,
1649 .label = "withdraw-coin-2",
1650 .expected_response_code = MHD_HTTP_OK,
1651 .details.reserve_withdraw.reserve_reference = "create-reserve-2",
1652 .details.reserve_withdraw.amount = concat_amount (currency, "5") },
1653
1654 /* Withdraw a 5 EUR coin, at fee of 1 ct */
1655 { .oc = OC_WITHDRAW_SIGN,
1656 .label = "withdraw-coin-3",
1657 .expected_response_code = MHD_HTTP_OK,
1658 .details.reserve_withdraw.reserve_reference = "create-reserve-3",
1659 .details.reserve_withdraw.amount = concat_amount (currency, "5") },
1660
1661 /* Create proposal */
1662 { .oc = OC_PROPOSAL,
1663 .label = "create-proposal-1",
1664 .expected_response_code = MHD_HTTP_OK,
1665 .details.proposal.max_fee = concat_amount (currency, "0.5"),
1666 .details.proposal.amount = concat_amount (currency, "0.5") },
1667
1668 /* Create proposal */
1669 { .oc = OC_PROPOSAL,
1670 .label = "create-proposal-2",
1671 .expected_response_code = MHD_HTTP_OK,
1672 .details.proposal.max_fee = concat_amount (currency, "0.5"),
1673 .details.proposal.amount = concat_amount (currency, "0.5") },
1674
1675 /* Create proposal */
1676 { .oc = OC_PROPOSAL,
1677 .label = "create-proposal-3",
1678 .expected_response_code = MHD_HTTP_OK,
1679 .details.proposal.max_fee = concat_amount (currency, "0.5"),
1680 .details.proposal.amount = concat_amount (currency, "5.0") },
1681
1682 { .oc = OC_PAY,
1683 .label = "deposit-simple-1",
1684 .expected_response_code = MHD_HTTP_OK,
1685 .details.pay.contract_ref = "create-proposal-1",
1686 .details.pay.coin_ref = "withdraw-coin-1",
1687 .details.pay.amount_with_fee = concat_amount (currency, "5"),
1688 .details.pay.amount_without_fee = concat_amount (currency, "4.99") },
1689
1690 { .oc = OC_PAY,
1691 .label = "deposit-simple-2",
1692 .expected_response_code = MHD_HTTP_OK,
1693 .details.pay.contract_ref = "create-proposal-2",
1694 .details.pay.coin_ref = "withdraw-coin-2",
1695 .details.pay.amount_with_fee = concat_amount (currency, "5"),
1696 .details.pay.amount_without_fee = concat_amount (currency, "4.99") },
1697
1698 { .oc = OC_PAY,
1699 .label = "deposit-simple-3",
1700 .expected_response_code = MHD_HTTP_OK,
1701 .details.pay.contract_ref = "create-proposal-3",
1702 .details.pay.coin_ref = "withdraw-coin-3",
1703 .details.pay.amount_with_fee = concat_amount (currency, "5"),
1704 .details.pay.amount_without_fee = concat_amount (currency, "4.99") },
1705
1706 { .oc = OC_END,
1707 .label = "end-of-commands"}
1708 };
1709
1710 is = GNUNET_new (struct InterpreterState);
1711 is->commands = GNUNET_malloc (sizeof (commands));
1712 memcpy (is->commands,
1713 commands,
1714 sizeof (commands));
1715 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1716 &rc);
1717 GNUNET_assert (NULL != ctx);
1718 rc = GNUNET_CURL_gnunet_rc_create (ctx);
1719 exchange = TALER_EXCHANGE_connect (ctx,
1720 exchange_url,
1721 &cert_cb,
1722 is,
1723 TALER_EXCHANGE_OPTION_END);
1724 GNUNET_assert (NULL != exchange);
1725 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1726 is);
1727}
1728 570
571 GNUNET_assert (GNUNET_SYSERR != result);
572 loglev = NULL;
573 GNUNET_log_setup ("taler-merchant-generate-payments-new",
574 loglev,
575 logfile);
1729 576
1730/** 577 if (NULL == cfg_filename)
1731 * Main function that will be run by the scheduler. 578 cfg_filename = (char *) default_config_file;
1732 *
1733 * @param cls closure
1734 * @param args remaining command-line arguments
1735 * @param cfgfile name of the configuration file used (for saving, can be
1736 * NULL!)
1737 * @param config configuration
1738 */
1739static void
1740run (void *cls,
1741 char *const *args,
1742 const char *cfgfile,
1743 const struct GNUNET_CONFIGURATION_Handle *config)
1744{
1745 unsigned int cnt;
1746 char *wget_cmd;
1747 579
1748 cfgfilename = GNUNET_strdup (cfgfile); 580 cfg = GNUNET_CONFIGURATION_create ();
1749 if (! remote_bank) 581 if (GNUNET_OK != GNUNET_CONFIGURATION_load
582 (cfg,
583 cfg_filename))
1750 { 584 {
1751 /* TODO: do not hard-code port, find in cfg */ 585 TALER_LOG_ERROR ("Could not parse configuration\n");
1752 fakebank = TALER_FAKEBANK_start (8888); 586 return BAD_CONFIG_FILE;
1753 if (NULL == fakebank)
1754 {
1755 fprintf (stderr,
1756 "Failed to launch fakebank\n");
1757 GNUNET_SCHEDULER_shutdown ();
1758 }
1759 }
1760 if (GNUNET_SYSERR ==
1761 GNUNET_CONFIGURATION_get_value_string (config,
1762 "payments-generator",
1763 "exchange",
1764 &exchange_url))
1765 {
1766 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1767 "payments-generator",
1768 "exchange");
1769 GNUNET_SCHEDULER_shutdown ();
1770 return;
1771 }
1772 if (GNUNET_SYSERR ==
1773 GNUNET_CONFIGURATION_get_value_string (config,
1774 "payments-generator",
1775 "exchange_admin",
1776 &exchange_url_admin))
1777 {
1778 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1779 "payments-generator",
1780 "exchange_admin");
1781 GNUNET_SCHEDULER_shutdown ();
1782 return;
1783 }
1784
1785 if (GNUNET_SYSERR ==
1786 GNUNET_CONFIGURATION_get_value_string (config,
1787 "payments-generator",
1788 "merchant",
1789 &merchant_url))
1790 {
1791 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1792 "payments-generator",
1793 "merchant");
1794 GNUNET_SCHEDULER_shutdown ();
1795 return;
1796 } 587 }
1797 588 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string
1798 if (GNUNET_SYSERR == 589 (cfg,
1799 GNUNET_CONFIGURATION_get_value_string (config, 590 "taler",
1800 "payments-generator", 591 "currency",
1801 "bank", 592 &currency))
1802 &bank_url))
1803 { 593 {
1804 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 594 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1805 "payments-generator", 595 "taler",
1806 "bank"); 596 "currency");
1807 GNUNET_SCHEDULER_shutdown (); 597 GNUNET_CONFIGURATION_destroy (cfg);
1808 return; 598 return BAD_CONFIG_FILE;
1809 } 599 }
600 GNUNET_CONFIGURATION_destroy (cfg);
1810 601
1811 if (GNUNET_SYSERR == 602 if (NULL == merchant_url)
1812 GNUNET_CONFIGURATION_get_value_string (config,
1813 "payments-generator",
1814 "instance",
1815 &instance))
1816 { 603 {
1817 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 604 TALER_LOG_ERROR ("Option -m is mandatory!\n");
1818 "payments-generator", 605 return MISSING_MERCHANT_URL;
1819 "instance");
1820 GNUNET_SCHEDULER_shutdown ();
1821 return;
1822 } 606 }
1823 607
1824 if (GNUNET_SYSERR == 608 if (NULL == (merchantd = TALER_TESTING_run_merchant
1825 GNUNET_CONFIGURATION_get_value_string (config, 609 (cfg_filename, merchant_url)))
1826 "payments-generator",
1827 "currency",
1828 &currency))
1829 { 610 {
1830 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 611 TALER_LOG_ERROR ("Failed to launch the merchant\n");
1831 "payments-generator", 612 return FAILED_TO_LAUNCH_MERCHANT;
1832 "currency");
1833 GNUNET_SCHEDULER_shutdown ();
1834 return;
1835 } 613 }
1836 614
1837 if (! remote_exchange) 615 if (NULL == bank_url)
1838 { 616 {
1839 exchanged = GNUNET_OS_start_process (GNUNET_NO, 617 TALER_LOG_ERROR ("Option -b is mandatory!\n");
1840 GNUNET_OS_INHERIT_STD_ALL, 618 return MISSING_BANK_URL;
1841 NULL, NULL, NULL,
1842 "taler-exchange-httpd",
1843 "taler-exchange-httpd",
1844 NULL);
1845 if (NULL == exchanged)
1846 {
1847 fprintf (stderr,
1848 "Failed to run taler-exchange-httpd. Check your PATH.\n");
1849 GNUNET_SCHEDULER_shutdown ();
1850 return;
1851 }
1852
1853 fprintf (stderr,
1854 "Waiting for taler-exchange-httpd to be ready\n");
1855 cnt = 0;
1856
1857 GNUNET_asprintf (&wget_cmd,
1858 "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null",
1859 exchange_url);
1860
1861 do
1862 {
1863 fprintf (stderr, ".");
1864 sleep (1);
1865 cnt++;
1866 if (cnt > 60)
1867 {
1868 fprintf (stderr,
1869 "\nFailed to start taler-exchange-httpd\n");
1870 GNUNET_OS_process_kill (exchanged,
1871 SIGKILL);
1872 GNUNET_OS_process_wait (exchanged);
1873 GNUNET_OS_process_destroy (exchanged);
1874 GNUNET_SCHEDULER_shutdown ();
1875 return;
1876 }
1877 }
1878 while (0 != system (wget_cmd));
1879 GNUNET_free (wget_cmd);
1880
1881 fprintf (stderr, "\n");
1882 } 619 }
1883 620
1884 if (! remote_merchant) 621 if ( NULL == (bankd = TALER_TESTING_run_bank
622 (cfg_filename,
623 bank_url)))
1885 { 624 {
1886 merchantd = GNUNET_OS_start_process (GNUNET_NO, 625 TALER_LOG_ERROR ("Failed to run the bank\n");
1887 GNUNET_OS_INHERIT_STD_ALL, 626 terminate_process (bankd);
1888 NULL, NULL, NULL, 627 terminate_process (merchantd);
1889 "taler-merchant-httpd", 628 return FAILED_TO_LAUNCH_BANK;
1890 "taler-merchant-httpd",
1891 "-L", "DEBUG",
1892 NULL);
1893 if (NULL == merchantd)
1894 {
1895 fprintf (stderr,
1896 "Failed to run taler-merchant-httpd. Check your PATH.\n");
1897 GNUNET_OS_process_kill (exchanged,
1898 SIGKILL);
1899 GNUNET_OS_process_wait (exchanged);
1900 GNUNET_OS_process_destroy (exchanged);
1901 GNUNET_SCHEDULER_shutdown ();
1902 return;
1903 }
1904 /* give child time to start and bind against the socket */
1905 fprintf (stderr,
1906 "Waiting for taler-merchant-httpd to be ready\n");
1907 cnt = 0;
1908 GNUNET_asprintf (&wget_cmd,
1909 "wget -q -t 1 -T 1 %s -o /dev/null -O /dev/null",
1910 merchant_url);
1911
1912 do
1913 {
1914 fprintf (stderr, ".");
1915 sleep (1);
1916 cnt++;
1917 if (cnt > 60)
1918 {
1919 fprintf (stderr,
1920 "\nFailed to start taler-merchant-httpd\n");
1921 GNUNET_OS_process_kill (merchantd,
1922 SIGKILL);
1923 GNUNET_OS_process_wait (merchantd);
1924 GNUNET_OS_process_destroy (merchantd);
1925 GNUNET_OS_process_kill (exchanged,
1926 SIGKILL);
1927 GNUNET_OS_process_wait (exchanged);
1928 GNUNET_OS_process_destroy (exchanged);
1929 GNUNET_SCHEDULER_shutdown ();
1930 return;
1931 }
1932 }
1933 while (0 != system (wget_cmd));
1934 fprintf (stderr, "\n");
1935 GNUNET_free (wget_cmd);
1936 } 629 }
1937 630
1938 shc_chld = GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, 631 result = TALER_TESTING_setup_with_exchange
1939 &sighandler_child_death); 632 (run,
1940 633 NULL,
1941 /* timeout, given 60s + 5s per command, which should be more 634 cfg_filename);
1942 than enough */
1943 timeout_task
1944 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_add
1945 (GNUNET_TIME_UNIT_MINUTES,
1946 GNUNET_TIME_relative_multiply
1947 (GNUNET_TIME_UNIT_SECONDS,
1948 5 * times)),
1949 &do_timeout, NULL);
1950 run_test ();
1951}
1952 635
636 terminate_process (merchantd);
637 terminate_process (bankd);
1953 638
1954int 639 return (GNUNET_OK == result) ? 0 : result;
1955main (int argc,
1956 char *argv[])
1957{
1958 struct GNUNET_GETOPT_CommandLineOption options[] = {
1959 GNUNET_GETOPT_option_uint ('n',
1960 "times",
1961 "TIMES",
1962 "How many times the commands should be run.",
1963 &times),
1964 GNUNET_GETOPT_option_flag ('b',
1965 "remote-bank",
1966 "Do not start fakebank",
1967 &remote_bank),
1968 GNUNET_GETOPT_option_flag ('e',
1969 "remote-exchange",
1970 "Do not fork any exchange",
1971 &remote_exchange),
1972 GNUNET_GETOPT_option_flag ('m',
1973 "remote-merchant",
1974 "Do not fork any merchant",
1975 &remote_merchant),
1976 GNUNET_GETOPT_OPTION_END
1977 };
1978
1979 unsetenv ("XDG_DATA_HOME");
1980 unsetenv ("XDG_CONFIG_HOME");
1981 GNUNET_log_setup ("taler-merchant-generate-payments",
1982 "DEBUG",
1983 NULL);
1984 result = GNUNET_SYSERR;
1985
1986 sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
1987 GNUNET_NO, GNUNET_NO);
1988 GNUNET_assert (NULL != sigpipe);
1989
1990 if (GNUNET_OK !=
1991 GNUNET_PROGRAM_run (argc, argv,
1992 "taler-merchant-generate-payments",
1993 "Populates DB with fake payments",
1994 options,
1995 &run, NULL))
1996 return 77;
1997
1998 if (NULL != shc_chld)
1999 {
2000 GNUNET_SIGNAL_handler_uninstall (shc_chld);
2001 shc_chld = NULL;
2002 }
2003 GNUNET_DISK_pipe_close (sigpipe);
2004 if (!remote_merchant && NULL != merchantd)
2005 {
2006 GNUNET_OS_process_kill (merchantd,
2007 SIGTERM);
2008 GNUNET_OS_process_wait (merchantd);
2009 GNUNET_OS_process_destroy (merchantd);
2010 }
2011 if (!remote_exchange && NULL != exchanged)
2012 {
2013 GNUNET_OS_process_kill (exchanged,
2014 SIGTERM);
2015 GNUNET_OS_process_wait (exchanged);
2016 GNUNET_OS_process_destroy (exchanged);
2017 }
2018 if (77 == result)
2019 return 77;
2020 return (GNUNET_OK == result) ? 0 : 1;
2021} 640}
diff --git a/src/merchant-tools/taler-merchant-generate-payments_new.c b/src/merchant-tools/taler-merchant-generate-payments_new.c
deleted file mode 100644
index db27b212..00000000
--- a/src/merchant-tools/taler-merchant-generate-payments_new.c
+++ /dev/null
@@ -1,640 +0,0 @@
1/*
2 This file is part of TALER
3 (C) 2014-2018 Taler Systems SA
4
5 TALER is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Affero 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 GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TALER; see the file COPYING. If not,
17 see <http://www.gnu.org/licenses/>
18*/
19
20/**
21 * @file merchant/backend/taler-merchant-httpd.c
22 * @brief HTTP serving layer intended to perform crypto-work and
23 * communication with the exchange
24 * @author Marcello Stanisci
25 */
26
27#include "platform.h"
28#include <taler/taler_util.h>
29#include <taler/taler_signatures.h>
30#include <taler/taler_exchange_service.h>
31#include <taler/taler_json_lib.h>
32#include <gnunet/gnunet_util_lib.h>
33#include <microhttpd.h>
34#include <taler/taler_bank_service.h>
35#include <taler/taler_fakebank_lib.h>
36#include <taler/taler_testing_lib.h>
37#include <taler/taler_testing_bank_lib.h>
38#include <taler/taler_error_codes.h>
39#include "taler_merchant_testing_lib.h"
40
41/* Error codes. */
42enum PaymentGeneratorError {
43
44 MISSING_MERCHANT_URL = 2,
45 FAILED_TO_LAUNCH_MERCHANT,
46 MISSING_BANK_URL,
47 FAILED_TO_LAUNCH_BANK,
48 BAD_CLI_ARG,
49 BAD_CONFIG_FILE
50};
51
52/* Hard-coded params. Note, the bank is expected to
53 * have the Tor user with account number 3 and password 'x'.
54 *
55 * This is not a problem _so far_, as the fakebank mocks logins,
56 * and the Python bank makes that account by default. */
57#define USER_ACCOUNT_NO 3
58#define EXCHANGE_ACCOUNT_NO 2
59#define USER_LOGIN_NAME "Tor"
60#define USER_LOGIN_PASS "x"
61#define EXCHANGE_URL "http://example.com/"
62
63#define FIRST_INSTRUCTION -1
64#define TRACKS_INSTRUCTION 9
65
66#define CMD_TRANSFER_TO_EXCHANGE(label,amount) \
67 TALER_TESTING_cmd_fakebank_transfer (label, amount, \
68 bank_url, USER_ACCOUNT_NO, EXCHANGE_ACCOUNT_NO, \
69 USER_LOGIN_NAME, USER_LOGIN_PASS, EXCHANGE_URL)
70
71/**
72 * Exit code.
73 */
74static unsigned int result;
75
76/**
77 * Bank process.
78 */
79static struct GNUNET_OS_Process *bankd;
80
81/**
82 * Merchant process.
83 */
84static struct GNUNET_OS_Process *merchantd;
85
86/**
87 * How many payments we want to generate.
88 */
89static unsigned int payments_number = 1;
90
91/**
92 * How many /tracks operation we want to perform.
93 */
94static unsigned int tracks_number = 1;
95
96
97/**
98 * Usually set as ~/.config/taler.net
99 */
100static const char *default_config_file;
101
102/**
103 * Log level used during the run.
104 */
105static char *loglev;
106
107/**
108 * Config filename.
109 */
110static char *cfg_filename;
111
112/**
113 * Bank base URL.
114 */
115static char *bank_url;
116
117/**
118 * Log file.
119 */
120static char *logfile;
121
122/**
123 * Merchant base URL.
124 */
125static char *merchant_url;
126
127/**
128 * Currency used.
129 */
130static char *currency;
131
132/**
133 * Convenience macros to allocate all the currency-dependant
134 * strings; note that the argument list of the macro is ignored.
135 * It is kept as a way to make the macro more auto-descriptive
136 * where it is called.
137 */
138
139#define ALLOCATE_AMOUNTS(...) \
140 GNUNET_asprintf (&CURRENCY_25_05, \
141 "%s:25.05", \
142 currency); \
143 GNUNET_asprintf (&CURRENCY_10, \
144 "%s:10", \
145 currency); \
146 GNUNET_asprintf (&CURRENCY_9_98, \
147 "%s:9.98", \
148 currency); \
149 GNUNET_asprintf (&CURRENCY_5, \
150 "%s:5", \
151 currency); \
152 GNUNET_asprintf (&CURRENCY_4_99, \
153 "%s:4.99", \
154 currency); \
155 GNUNET_asprintf (&CURRENCY_0_02, \
156 "%s:0.02", \
157 currency); \
158 GNUNET_asprintf (&CURRENCY_0_01, \
159 "%s:0.01", \
160 currency);
161
162#define ALLOCATE_ORDERS(...) \
163 GNUNET_asprintf \
164 (&order_worth_5, \
165 "{\"max_fee\":\
166 {\"currency\":\"%s\",\
167 \"value\":0,\
168 \"fraction\":50000000},\
169 \"refund_deadline\":\"\\/Date(0)\\/\",\
170 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
171 \"amount\":\
172 {\"currency\":\"%s\",\
173 \"value\":5,\
174 \"fraction\":0},\
175 \"summary\": \"merchant-lib testcase\",\
176 \"fulfillment_url\": \"https://example.com/\",\
177 \"products\": [ {\"description\":\"ice cream\",\
178 \"value\":\"{%s:5}\"} ] }", \
179 currency, \
180 currency, \
181 currency); \
182 GNUNET_asprintf \
183 (&order_worth_10_2coins, \
184 "{\"max_fee\":\
185 {\"currency\":\"%s\",\
186 \"value\":0,\
187 \"fraction\":50000000},\
188 \"refund_deadline\":\"\\/Date(0)\\/\",\
189 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
190 \"amount\":\
191 {\"currency\":\"%s\",\
192 \"value\":10,\
193 \"fraction\":0},\
194 \"summary\": \"2-coins untracked payment\",\
195 \"fulfillment_url\": \"https://example.com/\",\
196 \"products\": [ {\"description\":\"2-coins payment\",\
197 \"value\":\"{%s:10}\"} ] }", \
198 currency, \
199 currency, \
200 currency); \
201 GNUNET_asprintf \
202 (&order_worth_5_track, \
203 "{\"max_fee\":\
204 {\"currency\":\"%s\",\
205 \"value\":0,\
206 \"fraction\":50000000},\
207 \"refund_deadline\":\"\\/Date(0)\\/\",\
208 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
209 \"amount\":\
210 {\"currency\":\"%s\",\
211 \"value\":5,\
212 \"fraction\":0},\
213 \"summary\": \"ice track cream!\",\
214 \"fulfillment_url\": \"https://example.com/\",\
215 \"products\": [ {\"description\":\"ice track cream\",\
216 \"value\":\"{%s:5}\"} ] }", \
217 currency, \
218 currency, \
219 currency); \
220 GNUNET_asprintf \
221 (&order_worth_5_unaggregated, \
222 "{\"max_fee\":\
223 {\"currency\":\"%s\",\
224 \"value\":0,\
225 \"fraction\":50000000},\
226 \"refund_deadline\":\"\\/Date(0)\\/\",\
227 \"pay_deadline\":\"\\/Date(99999999999)\\/\",\
228 \"amount\":\
229 {\"currency\":\"%s\",\
230 \"value\":5,\
231 \"fraction\":0},\
232 \"summary\": \"unaggregated deposit!\",\
233 \"fulfillment_url\": \"https://example.com/\",\
234 \"products\": [ {\"description\":\"unaggregated cream\",\
235 \"value\":\"{%s:5}\"} ] }", \
236 currency, \
237 currency, \
238 currency);
239
240/**
241 * Actual commands collection.
242 */
243static void
244run (void *cls,
245 struct TALER_TESTING_Interpreter *is)
246{
247
248 /* Currency strings. */
249 char *CURRENCY_25_05;
250 char *CURRENCY_10;
251 char *CURRENCY_9_98;
252 char *CURRENCY_5;
253 char *CURRENCY_4_99;
254 char *CURRENCY_0_02;
255 char *CURRENCY_0_01;
256
257 ALLOCATE_AMOUNTS
258 (CURRENCY_25_05,
259 CURRENCY_10,
260 CURRENCY_9_98,
261 CURRENCY_5,
262 CURRENCY_4_99,
263 CURRENCY_0_02,
264 CURRENCY_0_01);
265
266
267 /* Orders. */
268 char *order_worth_5;
269 char *order_worth_10_2coins;
270 char *order_worth_5_track;
271 char *order_worth_5_unaggregated;
272
273 ALLOCATE_ORDERS
274 (order_worth_5,
275 order_worth_10_2coins,
276 order_worth_5_track,
277 order_worth_5_unaggregated);
278
279 struct TALER_TESTING_Command commands[] = {
280
281 CMD_TRANSFER_TO_EXCHANGE
282 ("create-reserve-1",
283 CURRENCY_25_05),
284
285 TALER_TESTING_cmd_exec_wirewatch
286 ("wirewatch-1",
287 cfg_filename),
288
289 TALER_TESTING_cmd_withdraw_amount
290 ("withdraw-coin-1",
291 is->exchange, // picks port from config's [exchange].
292 "create-reserve-1",
293 CURRENCY_5,
294 MHD_HTTP_OK),
295
296 TALER_TESTING_cmd_withdraw_amount
297 ("withdraw-coin-2",
298 is->exchange,
299 "create-reserve-1",
300 CURRENCY_5,
301 MHD_HTTP_OK),
302
303 /* This coin will be spent but never aggregated,
304 * in order to get 202 responses from tracks. */
305 TALER_TESTING_cmd_withdraw_amount
306 ("withdraw-coin-3",
307 is->exchange,
308 "create-reserve-1",
309 CURRENCY_5,
310 MHD_HTTP_OK),
311
312 /* coin 4 & 5 will be deposited for the same
313 * contract; needed in case some testing utility
314 * wants to trigger a "failed dependency" error. */
315 TALER_TESTING_cmd_withdraw_amount
316 ("withdraw-coin-4",
317 is->exchange,
318 "create-reserve-1",
319 CURRENCY_5,
320 MHD_HTTP_OK),
321
322 TALER_TESTING_cmd_withdraw_amount
323 ("withdraw-coin-5",
324 is->exchange,
325 "create-reserve-1",
326 CURRENCY_5,
327 MHD_HTTP_OK),
328
329 TALER_TESTING_cmd_proposal
330 ("create-proposal-1",
331 merchant_url,
332 is->ctx,
333 MHD_HTTP_OK,
334 order_worth_5,
335 NULL),
336
337 TALER_TESTING_cmd_pay
338 ("deposit-simple",
339 merchant_url,
340 is->ctx,
341 MHD_HTTP_OK,
342 "create-proposal-1",
343 "withdraw-coin-1",
344 CURRENCY_5,
345 CURRENCY_4_99,
346 CURRENCY_0_01),
347
348 TALER_TESTING_cmd_rewind_ip
349 ("rewind-payments",
350 FIRST_INSTRUCTION,
351 &payments_number),
352
353 /* Next proposal-pay cycle will be used by /track CMDs
354 * and so it will not have to be looped over, only /track
355 * CMDs will have to. */
356
357 TALER_TESTING_cmd_proposal
358 ("create-proposal-2",
359 merchant_url,
360 is->ctx,
361 MHD_HTTP_OK,
362 order_worth_5_track,
363 NULL),
364
365 TALER_TESTING_cmd_pay
366 ("deposit-simple-2",
367 merchant_url,
368 is->ctx,
369 MHD_HTTP_OK,
370 "create-proposal-2",
371 "withdraw-coin-2",
372 CURRENCY_5,
373 CURRENCY_4_99,
374 CURRENCY_0_01),
375
376 /* /track/transaction over deposit-simple-2 */
377
378 TALER_TESTING_cmd_exec_aggregator
379 ("aggregate-1",
380 cfg_filename),
381
382 TALER_TESTING_cmd_merchant_track_transaction
383 ("track-transaction-1",
384 merchant_url,
385 is->ctx,
386 MHD_HTTP_OK,
387 "dummy", // "check bank" CMD, never used, to be deleted.
388 "deposit-simple-2",
389 CURRENCY_0_01),
390
391 TALER_TESTING_cmd_merchant_track_transfer
392 ("track-transfer-1",
393 merchant_url,
394 is->ctx,
395 MHD_HTTP_OK,
396 "track-transaction-1",
397 "deposit-simple-2"),
398
399 /* Doing the 2-coins payment; needed to generate the
400 * "failed dependency" response error, at /track/transaction.
401 * NOTE: not used here, but done just in case a testing
402 * program would need it. And this MUST happen here, as
403 * no tracking operation happens next and so the merchant
404 * won't be able to use a cached version in its database
405 * when serving /track/..; therefore it will relate to the
406 * exchange that can be twisted by the testing logic. */
407 TALER_TESTING_cmd_proposal
408 ("create-proposal-4&5",
409 merchant_url,
410 is->ctx,
411 MHD_HTTP_OK,
412 order_worth_10_2coins,
413 NULL),
414
415 TALER_TESTING_cmd_pay ("deposit-4&5",
416 merchant_url,
417 is->ctx,
418 MHD_HTTP_OK,
419 "create-proposal-4&5",
420 "withdraw-coin-4;" \
421 "withdraw-coin-5",
422 CURRENCY_10,
423 CURRENCY_9_98, // no sense now
424 CURRENCY_0_02), // no sense now
425
426 TALER_TESTING_cmd_exec_aggregator
427 ("aggregate-2",
428 cfg_filename),
429
430 /* Must be _after_ any aggregation takes place. */
431 TALER_TESTING_cmd_proposal
432 ("create-proposal-3",
433 merchant_url,
434 is->ctx,
435 MHD_HTTP_OK,
436 order_worth_5_unaggregated,
437 NULL),
438
439 TALER_TESTING_cmd_pay
440 ("deposit-simple-3",
441 merchant_url,
442 is->ctx,
443 MHD_HTTP_OK,
444 "create-proposal-3",
445 "withdraw-coin-3",
446 CURRENCY_5,
447 CURRENCY_4_99,
448 CURRENCY_0_01),
449
450 TALER_TESTING_cmd_merchant_track_transaction
451 ("track-transaction-2",
452 merchant_url,
453 is->ctx,
454 MHD_HTTP_ACCEPTED,
455 "dummy", // "check bank" CMD, never used, to be deleted.
456 "deposit-simple-3",
457 CURRENCY_0_01),
458
459 TALER_TESTING_cmd_rewind_ip
460 ("rewind-tracks",
461 TRACKS_INSTRUCTION,
462 &tracks_number),
463
464 TALER_TESTING_cmd_end ()
465 };
466
467 TALER_TESTING_run (is,
468 commands);
469}
470
471/**
472 * Send SIGTERM and wait for process termination.
473 *
474 * @param process process to terminate.
475 */
476void
477terminate_process (struct GNUNET_OS_Process *process)
478{
479 GNUNET_OS_process_kill (process, SIGTERM);
480 GNUNET_OS_process_wait (process);
481 GNUNET_OS_process_destroy (process);
482}
483
484/**
485 * The main function of the serve tool
486 *
487 * @param argc number of arguments from the command line
488 * @param argv command line arguments
489 * @return 0 ok, or `enum PaymentGeneratorError` on error
490 */
491int
492main (int argc,
493 char *const *argv)
494{
495 struct GNUNET_CONFIGURATION_Handle *cfg;
496
497 default_config_file = GNUNET_OS_project_data_get
498 ()->user_config_file;
499
500 struct GNUNET_GETOPT_CommandLineOption options[] = {
501
502 GNUNET_GETOPT_option_cfgfile
503 (&cfg_filename),
504
505 GNUNET_GETOPT_option_version
506 (PACKAGE_VERSION " " VCS_VERSION),
507
508 GNUNET_GETOPT_option_help
509 ("Generate Taler payments to populate the database(s)"),
510
511 GNUNET_GETOPT_option_loglevel
512 (&loglev),
513
514 GNUNET_GETOPT_option_uint
515 ('p',
516 "payments-number",
517 "PN",
518 "will generate PN payments, defaults to 1",
519 &payments_number),
520
521 GNUNET_GETOPT_option_uint
522 ('t',
523 "tracks-number",
524 "TN",
525 "will perform TN /track operations, defaults to 1",
526 &tracks_number),
527
528 /**
529 * NOTE: useful when the setup serves merchant
530 * backends via unix domain sockets, since there
531 * is no way - yet? - to get the merchant base url.
532 * Clearly, we could introduce a merchant_base_url
533 * value into the configuration.
534 */
535 GNUNET_GETOPT_option_string
536 ('m',
537 "merchant-url",
538 "MU",
539 "merchant base url, mandatory",
540 &merchant_url),
541
542 GNUNET_GETOPT_option_string
543 ('b',
544 "bank-url",
545 "BU",
546 "bank base url, mandatory",
547 &bank_url),
548
549 GNUNET_GETOPT_option_string
550 ('l',
551 "logfile",
552 "LF",
553 "will log to file LF",
554 &logfile),
555
556 GNUNET_GETOPT_OPTION_END
557 };
558
559 result = GNUNET_GETOPT_run
560 ("taler-merchant-generate-payments-new",
561 options,
562 argc,
563 argv);
564
565 if (GNUNET_NO == result)
566 {
567 /* --help or --version were given, just return. */
568 return 0;
569 }
570
571 GNUNET_assert (GNUNET_SYSERR != result);
572 loglev = NULL;
573 GNUNET_log_setup ("taler-merchant-generate-payments-new",
574 loglev,
575 logfile);
576
577 if (NULL == cfg_filename)
578 cfg_filename = (char *) default_config_file;
579
580 cfg = GNUNET_CONFIGURATION_create ();
581 if (GNUNET_OK != GNUNET_CONFIGURATION_load
582 (cfg,
583 cfg_filename))
584 {
585 TALER_LOG_ERROR ("Could not parse configuration\n");
586 return BAD_CONFIG_FILE;
587 }
588 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string
589 (cfg,
590 "taler",
591 "currency",
592 &currency))
593 {
594 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
595 "taler",
596 "currency");
597 GNUNET_CONFIGURATION_destroy (cfg);
598 return BAD_CONFIG_FILE;
599 }
600 GNUNET_CONFIGURATION_destroy (cfg);
601
602 if (NULL == merchant_url)
603 {
604 TALER_LOG_ERROR ("Option -m is mandatory!\n");
605 return MISSING_MERCHANT_URL;
606 }
607
608 if (NULL == (merchantd = TALER_TESTING_run_merchant
609 (cfg_filename, merchant_url)))
610 {
611 TALER_LOG_ERROR ("Failed to launch the merchant\n");
612 return FAILED_TO_LAUNCH_MERCHANT;
613 }
614
615 if (NULL == bank_url)
616 {
617 TALER_LOG_ERROR ("Option -b is mandatory!\n");
618 return MISSING_BANK_URL;
619 }
620
621 if ( NULL == (bankd = TALER_TESTING_run_bank
622 (cfg_filename,
623 bank_url)))
624 {
625 TALER_LOG_ERROR ("Failed to run the bank\n");
626 terminate_process (bankd);
627 terminate_process (merchantd);
628 return FAILED_TO_LAUNCH_BANK;
629 }
630
631 result = TALER_TESTING_setup_with_exchange
632 (run,
633 NULL,
634 cfg_filename);
635
636 terminate_process (merchantd);
637 terminate_process (bankd);
638
639 return (GNUNET_OK == result) ? 0 : result;
640}