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_api_cmd_pay_order.c
21 : * @brief command to test the /orders/ID/pay feature.
22 : * @author Marcello Stanisci
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include <gnunet/gnunet_common.h>
27 : #include <gnunet/gnunet_json_lib.h>
28 : #include <gnunet/gnunet_time_lib.h>
29 : #include <jansson.h>
30 : #include <stddef.h>
31 : #include <stdint.h>
32 : #include <taler/taler_exchange_service.h>
33 : #include <taler/taler_testing_lib.h>
34 : #include <taler/taler_signatures.h>
35 : #include "taler_merchant_service.h"
36 : #include "taler_merchant_testing_lib.h"
37 :
38 :
39 : /**
40 : * State for a /pay CMD.
41 : */
42 : struct PayState
43 : {
44 : /**
45 : * Contract terms hash code.
46 : */
47 : struct TALER_PrivateContractHashP h_contract_terms;
48 :
49 : /**
50 : * The interpreter state.
51 : */
52 : struct TALER_TESTING_Interpreter *is;
53 :
54 : /**
55 : * Expected HTTP response status code.
56 : */
57 : unsigned int http_status;
58 :
59 : /**
60 : * Reference to a command that can provide a order id,
61 : * typically a /proposal test command.
62 : */
63 : const char *proposal_reference;
64 :
65 : /**
66 : * Reference to a command that can provide a coin, so
67 : * we can pay here.
68 : */
69 : const char *coin_reference;
70 :
71 : /**
72 : * Reference to a command that can provide one or
73 : * multiple tokens used as inputs for the payment.
74 : * In the form "LABEL0[/INDEX];LABEL1[/INDEX];..."
75 : */
76 : const char *token_reference;
77 :
78 : /**
79 : * The merchant base URL.
80 : */
81 : const char *merchant_url;
82 :
83 : /**
84 : * Total amount to be paid.
85 : */
86 : struct TALER_Amount total_amount;
87 :
88 : /**
89 : * Amount to be paid, plus the deposit fee.
90 : */
91 : const char *amount_with_fee;
92 :
93 : /**
94 : * Amount to be paid, including NO fees.
95 : */
96 : const char *amount_without_fee;
97 :
98 : /**
99 : * Handle to the pay operation.
100 : */
101 : struct TALER_MERCHANT_OrderPayHandle *oph;
102 :
103 : /**
104 : * Signature from the merchant, set on success.
105 : */
106 : struct TALER_MerchantSignatureP merchant_sig;
107 :
108 : /**
109 : * Array of issued tokens, set on success.
110 : */
111 : struct TALER_MERCHANT_PrivateTokenDetails *issued_tokens;
112 :
113 : /**
114 : * Number of tokens in @e issued_tokens.
115 : */
116 : unsigned int num_issued_tokens;
117 :
118 : /**
119 : * The session for which the payment is made.
120 : */
121 : const char *session_id;
122 :
123 : /**
124 : * base64-encoded key
125 : */
126 : const char *pos_key;
127 :
128 : /**
129 : * Option that add amount of the order
130 : */
131 : enum TALER_MerchantConfirmationAlgorithm pos_alg;
132 :
133 : /**
134 : * Index of the choice to be used in the payment. -1 for orders without choices.
135 : */
136 : int choice_index;
137 :
138 : };
139 :
140 :
141 : /**
142 : * Find the token issue public key for a given token family @a slug and
143 : * @a valid_after timestamp.
144 : *
145 : * @param token_families json object of token families where the key is the slug
146 : * @param slug the slug of the token family
147 : * @param key_index index of the key within the token family
148 : * @param[out] pub the token issue public key of the token family
149 : * @return #GNUNET_OK on success and #GNUNET_SYSERR if not found
150 : */
151 : static enum GNUNET_GenericReturnValue
152 6 : find_token_public_key (const json_t *token_families,
153 : const char *slug,
154 : unsigned int key_index,
155 : struct TALER_TokenIssuePublicKey *pub)
156 :
157 : {
158 6 : const json_t *tf = json_object_get (token_families, slug);
159 : const json_t *keys;
160 : struct GNUNET_JSON_Specification spec[] = {
161 6 : GNUNET_JSON_spec_array_const ("keys",
162 : &keys),
163 6 : GNUNET_JSON_spec_end ()
164 : };
165 : const json_t *key;
166 : const char *error_name;
167 : unsigned int error_line;
168 : struct GNUNET_JSON_Specification ispec[] = {
169 6 : TALER_JSON_spec_token_pub (NULL,
170 : pub),
171 6 : GNUNET_JSON_spec_end ()
172 : };
173 :
174 6 : if (NULL == tf)
175 : {
176 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177 : "Token family `%s' not found\n",
178 : slug);
179 0 : return GNUNET_SYSERR;
180 : }
181 6 : if (GNUNET_OK !=
182 6 : GNUNET_JSON_parse (tf,
183 : spec,
184 : NULL,
185 : NULL))
186 : {
187 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
188 : "Failed to parse token family `%s'\n",
189 : slug);
190 0 : return GNUNET_SYSERR;
191 : }
192 :
193 6 : key = json_array_get (keys,
194 : key_index);
195 6 : if (NULL == key)
196 : {
197 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
198 : "Key with index %u for token family '%s' not found\n",
199 : key_index,
200 : slug);
201 0 : return GNUNET_SYSERR;
202 : }
203 6 : if (GNUNET_OK !=
204 6 : GNUNET_JSON_parse (key,
205 : ispec,
206 : &error_name,
207 : &error_line))
208 : {
209 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
210 : "Failed to parse %s at %u: %s\n",
211 : ispec[error_line].field,
212 : error_line,
213 : error_name);
214 0 : return GNUNET_SYSERR;
215 : }
216 6 : return GNUNET_OK;
217 : }
218 :
219 :
220 : /**
221 : * Parse the @a coins specification and grow the @a pc
222 : * array with the coins found, updating @a npc.
223 : *
224 : * @param[in,out] pc pointer to array of coins found
225 : * @param[in,out] npc length of array at @a pc
226 : * @param[in] coins string specifying coins to add to @a pc,
227 : * clobbered in the process
228 : * @param is interpreter state
229 : * @param amount_with_fee total amount to be paid for a contract.
230 : * @param amount_without_fee to be removed, there is no
231 : * per-contract fee, only per-coin exists.
232 : * @return #GNUNET_OK on success
233 : */
234 : static enum GNUNET_GenericReturnValue
235 34 : build_coins (struct TALER_MERCHANT_PayCoin **pc,
236 : unsigned int *npc,
237 : char *coins,
238 : struct TALER_TESTING_Interpreter *is,
239 : const char *amount_with_fee,
240 : const char *amount_without_fee)
241 : {
242 : char *token;
243 : struct TALER_EXCHANGE_Keys *keys;
244 :
245 34 : keys = TALER_TESTING_get_keys (is);
246 34 : if (NULL == keys)
247 : {
248 0 : GNUNET_break (0);
249 0 : return GNUNET_SYSERR;
250 : }
251 :
252 34 : for (token = strtok (coins, ";");
253 74 : NULL != token;
254 40 : token = strtok (NULL, ";"))
255 : {
256 : const struct TALER_TESTING_Command *coin_cmd;
257 : char *ctok;
258 : unsigned int ci;
259 : struct TALER_MERCHANT_PayCoin *icoin;
260 : const struct TALER_EXCHANGE_DenomPublicKey *dpk;
261 : const char *exchange_url;
262 :
263 : /* Token syntax is "LABEL[/NUMBER]" */
264 40 : ctok = strchr (token, '/');
265 : /* FIXME: Check why ci variable is parsed but not used? */
266 40 : ci = 0;
267 40 : if (NULL != ctok)
268 : {
269 0 : *ctok = '\0';
270 0 : ctok++;
271 0 : if (1 != sscanf (ctok,
272 : "%u",
273 : &ci))
274 : {
275 0 : GNUNET_break (0);
276 0 : return GNUNET_SYSERR;
277 : }
278 : }
279 :
280 40 : coin_cmd = TALER_TESTING_interpreter_lookup_command
281 : (is, token);
282 :
283 40 : if (NULL == coin_cmd)
284 : {
285 0 : GNUNET_break (0);
286 0 : return GNUNET_SYSERR;
287 : }
288 :
289 40 : GNUNET_array_grow (*pc,
290 : *npc,
291 : (*npc) + 1);
292 :
293 40 : icoin = &((*pc)[(*npc) - 1]);
294 :
295 : {
296 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
297 : const struct TALER_DenominationSignature *denom_sig;
298 : const struct TALER_Amount *denom_value;
299 : const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
300 : const struct TALER_AgeCommitmentHashP *h_age_commitment;
301 :
302 40 : GNUNET_assert (GNUNET_OK ==
303 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
304 : 0,
305 : &coin_priv));
306 40 : GNUNET_assert (GNUNET_OK ==
307 : TALER_TESTING_get_trait_denom_pub (coin_cmd,
308 : 0,
309 : &denom_pub));
310 40 : GNUNET_assert (GNUNET_OK ==
311 : TALER_TESTING_get_trait_denom_sig (coin_cmd,
312 : 0,
313 : &denom_sig));
314 40 : GNUNET_assert (GNUNET_OK ==
315 : TALER_TESTING_get_trait_amount (coin_cmd,
316 : &denom_value));
317 40 : GNUNET_assert (GNUNET_OK ==
318 : TALER_TESTING_get_trait_h_age_commitment (coin_cmd,
319 : 0,
320 : &h_age_commitment
321 : ));
322 40 : icoin->coin_priv = *coin_priv;
323 40 : icoin->denom_pub = denom_pub->key;
324 40 : icoin->denom_sig = *denom_sig;
325 40 : icoin->denom_value = *denom_value;
326 40 : icoin->amount_with_fee = *denom_value;
327 40 : icoin->h_age_commitment = h_age_commitment;
328 : }
329 40 : GNUNET_assert (NULL != (dpk =
330 : TALER_TESTING_find_pk (keys,
331 : &icoin->denom_value,
332 : false)));
333 :
334 40 : GNUNET_assert (0 <=
335 : TALER_amount_subtract (&icoin->amount_without_fee,
336 : &icoin->denom_value,
337 : &dpk->fees.deposit));
338 40 : GNUNET_assert (GNUNET_OK ==
339 : TALER_TESTING_get_trait_exchange_url (coin_cmd,
340 : &exchange_url));
341 40 : icoin->exchange_url = exchange_url;
342 : }
343 :
344 34 : return GNUNET_OK;
345 : }
346 :
347 :
348 : /**
349 : * Parse the @a pay_references specification and grow the @a tokens
350 : * array with the tokens found, updating @a tokens_num.
351 : *
352 : * @param[in,out] tokens array of tokens found
353 : * @param[in,out] tokens_num length of @a tokens array
354 : * @param[in] pay_references string of ; separated references to pay commands
355 : that issued the tokens.
356 : * @param is interpreter state
357 : * @return #GNUNET_OK on success
358 : */
359 : static enum GNUNET_GenericReturnValue
360 4 : build_tokens (struct TALER_MERCHANT_UseToken **tokens,
361 : unsigned int *tokens_num,
362 : char *pay_references,
363 : struct TALER_TESTING_Interpreter *is)
364 : {
365 : char *ref;
366 :
367 4 : for (ref = strtok (pay_references, ";");
368 8 : NULL != ref;
369 4 : ref = strtok (NULL, ";"))
370 : {
371 : const struct TALER_TESTING_Command *pay_cmd;
372 : char *slash;
373 : unsigned int index;
374 : struct TALER_MERCHANT_UseToken *token;
375 :
376 : /* Reference syntax is "LABEL[/NUMBER]" */
377 4 : slash = strchr (ref, '/');
378 4 : index = 0;
379 4 : if (NULL != slash)
380 : {
381 0 : *slash = '\0';
382 0 : slash++;
383 0 : if (1 != sscanf (slash,
384 : "%u",
385 : &index))
386 : {
387 0 : GNUNET_break (0);
388 0 : return GNUNET_SYSERR;
389 : }
390 : }
391 :
392 4 : pay_cmd = TALER_TESTING_interpreter_lookup_command (is, ref);
393 :
394 4 : if (NULL == pay_cmd)
395 : {
396 0 : GNUNET_break (0);
397 0 : return GNUNET_SYSERR;
398 : }
399 :
400 4 : GNUNET_array_grow (*tokens,
401 : *tokens_num,
402 : (*tokens_num) + 1);
403 :
404 4 : token = &((*tokens)[(*tokens_num) - 1]);
405 :
406 : {
407 : const struct TALER_TokenUsePrivateKeyP *token_priv;
408 : const struct TALER_TokenIssueSignature *issue_sig;
409 : const struct TALER_TokenIssuePublicKey *issue_pub;
410 :
411 4 : GNUNET_assert (GNUNET_OK ==
412 : TALER_TESTING_get_trait_token_priv (pay_cmd,
413 : index,
414 : &token_priv));
415 :
416 4 : GNUNET_assert (GNUNET_OK ==
417 : TALER_TESTING_get_trait_token_issue_sig (pay_cmd,
418 : index,
419 : &issue_sig));
420 :
421 4 : GNUNET_assert (GNUNET_OK ==
422 : TALER_TESTING_get_trait_token_issue_pub (pay_cmd,
423 : index,
424 : &issue_pub));
425 :
426 4 : token->token_priv = *token_priv;
427 4 : token->ub_sig = *issue_sig;
428 4 : token->issue_pub = *issue_pub;
429 : }
430 : }
431 :
432 4 : return GNUNET_OK;
433 : }
434 :
435 :
436 : /**
437 : * Function called with the result of a /pay operation.
438 : * Checks whether the merchant signature is valid and the
439 : * HTTP response code matches our expectation.
440 : *
441 : * @param cls closure with the interpreter state
442 : * @param pr HTTP response
443 : */
444 : static void
445 34 : pay_cb (void *cls,
446 : const struct TALER_MERCHANT_PayResponse *pr)
447 : {
448 34 : struct PayState *ps = cls;
449 :
450 34 : ps->oph = NULL;
451 34 : if (ps->http_status != pr->hr.http_status)
452 : {
453 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454 : "Unexpected response code %u (%d) to command (%s) %s\n",
455 : pr->hr.http_status,
456 : (int) pr->hr.ec,
457 : pr->hr.hint,
458 : TALER_TESTING_interpreter_get_current_label (ps->is));
459 0 : TALER_TESTING_FAIL (ps->is);
460 : }
461 34 : if (MHD_HTTP_OK == pr->hr.http_status)
462 : {
463 22 : ps->merchant_sig = pr->details.ok.merchant_sig;
464 22 : if (ps->num_issued_tokens != pr->details.ok.num_tokens)
465 : {
466 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
467 : "Unexpected number of tokens issued. "
468 : "Sent %d envelopes but got %d tokens issued.\n",
469 : ps->num_issued_tokens,
470 : pr->details.ok.num_tokens);
471 0 : GNUNET_break (0);
472 0 : TALER_TESTING_interpreter_fail (ps->is);
473 0 : return;
474 : }
475 26 : for (unsigned int i = 0; i < ps->num_issued_tokens; i++)
476 : {
477 4 : struct TALER_MERCHANT_PrivateTokenDetails *details =
478 4 : &ps->issued_tokens[i];
479 :
480 : /* The issued tokens should be in the
481 : same order as the provided envelopes. */
482 4 : ps->issued_tokens[i].blinded_sig = pr->details.ok.tokens[i].blinded_sig;
483 :
484 4 : if (GNUNET_OK !=
485 4 : TALER_token_issue_sig_unblind (&details->issue_sig,
486 4 : &details->blinded_sig,
487 4 : &details->blinding_secret,
488 4 : &details->h_token_pub,
489 4 : &details->blinding_inputs,
490 4 : &details->issue_pub))
491 : {
492 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
493 : "Failed to unblind token signature\n");
494 0 : GNUNET_break (0);
495 0 : TALER_TESTING_interpreter_fail (ps->is);
496 0 : return;
497 : }
498 : }
499 22 : if (NULL != ps->pos_key)
500 : {
501 : char *pc;
502 2 : bool found = false;
503 :
504 2 : if (NULL == pr->details.ok.pos_confirmation)
505 : {
506 0 : GNUNET_break (0);
507 0 : TALER_TESTING_interpreter_fail (ps->is);
508 0 : return;
509 : }
510 2 : pc = TALER_build_pos_confirmation (ps->pos_key,
511 : ps->pos_alg,
512 2 : &ps->total_amount,
513 : GNUNET_TIME_timestamp_get ());
514 : /* Check if *any* of our TOTP codes overlaps
515 : with any of the returned TOTP codes. */
516 2 : for (const char *tok = strtok (pc, "\n");
517 2 : NULL != tok;
518 0 : tok = strtok (NULL, "\n"))
519 : {
520 2 : if (NULL != strstr (pr->details.ok.pos_confirmation,
521 : tok))
522 : {
523 2 : found = true;
524 2 : break;
525 : }
526 : }
527 2 : GNUNET_free (pc);
528 2 : if (! found)
529 : {
530 0 : GNUNET_break (0);
531 0 : TALER_TESTING_interpreter_fail (ps->is);
532 0 : return;
533 : }
534 : }
535 : }
536 34 : TALER_TESTING_interpreter_next (ps->is);
537 : }
538 :
539 :
540 : /**
541 : * Run a "pay" CMD.
542 : *
543 : * @param cls closure.
544 : * @param cmd current CMD being run.
545 : * @param is interpreter state.
546 : */
547 : static void
548 34 : pay_run (void *cls,
549 : const struct TALER_TESTING_Command *cmd,
550 : struct TALER_TESTING_Interpreter *is)
551 : {
552 34 : struct PayState *ps = cls;
553 : const struct TALER_TESTING_Command *proposal_cmd;
554 : const json_t *contract_terms;
555 : const char *order_id;
556 : struct GNUNET_TIME_Timestamp refund_deadline;
557 : struct GNUNET_TIME_Timestamp pay_deadline;
558 : struct GNUNET_TIME_Timestamp timestamp;
559 : struct TALER_MerchantPublicKeyP merchant_pub;
560 : struct TALER_MerchantWireHashP h_wire;
561 : const struct TALER_PrivateContractHashP *h_proposal;
562 : struct TALER_Amount max_fee;
563 34 : const char *error_name = NULL;
564 34 : unsigned int error_line = 0;
565 : struct TALER_MERCHANT_PayCoin *pay_coins;
566 : unsigned int npay_coins;
567 34 : struct TALER_MERCHANT_UseToken *use_tokens = NULL;
568 34 : unsigned int len_use_tokens = 0;
569 34 : struct TALER_MERCHANT_OutputToken *output_tokens = NULL;
570 34 : unsigned int len_output_tokens = 0;
571 : const struct TALER_MerchantSignatureP *merchant_sig;
572 : const enum TALER_MerchantConfirmationAlgorithm *alg_ptr;
573 :
574 34 : ps->is = is;
575 34 : proposal_cmd = TALER_TESTING_interpreter_lookup_command (
576 : is,
577 : ps->proposal_reference);
578 :
579 34 : if (NULL == proposal_cmd)
580 0 : TALER_TESTING_FAIL (is);
581 :
582 34 : if (GNUNET_OK !=
583 34 : TALER_TESTING_get_trait_contract_terms (proposal_cmd,
584 : &contract_terms))
585 0 : TALER_TESTING_FAIL (is);
586 34 : if (NULL == contract_terms)
587 0 : TALER_TESTING_FAIL (is);
588 34 : if (GNUNET_OK !=
589 34 : TALER_TESTING_get_trait_otp_key (proposal_cmd,
590 : &ps->pos_key))
591 30 : ps->pos_key = NULL;
592 34 : if ( (GNUNET_OK ==
593 34 : TALER_TESTING_get_trait_otp_alg (proposal_cmd,
594 4 : &alg_ptr)) &&
595 4 : (NULL != alg_ptr) )
596 2 : ps->pos_alg = *alg_ptr;
597 : {
598 : /* Get information that needs to be put verbatim in the
599 : * deposit permission */
600 34 : uint64_t version = 0;
601 : struct GNUNET_JSON_Specification spec[] = {
602 34 : GNUNET_JSON_spec_mark_optional (
603 : GNUNET_JSON_spec_uint64 ("version",
604 : &version),
605 : NULL),
606 34 : GNUNET_JSON_spec_string ("order_id",
607 : &order_id),
608 34 : GNUNET_JSON_spec_timestamp ("refund_deadline",
609 : &refund_deadline),
610 34 : GNUNET_JSON_spec_timestamp ("pay_deadline",
611 : &pay_deadline),
612 34 : GNUNET_JSON_spec_timestamp ("timestamp",
613 : ×tamp),
614 34 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
615 : &merchant_pub),
616 34 : GNUNET_JSON_spec_fixed_auto ("h_wire",
617 : &h_wire),
618 : /* FIXME oec: parse minimum age, use data later? */
619 34 : GNUNET_JSON_spec_end ()
620 : };
621 :
622 34 : if (GNUNET_OK !=
623 34 : GNUNET_JSON_parse (contract_terms,
624 : spec,
625 : &error_name,
626 : &error_line))
627 : {
628 : char *js;
629 :
630 0 : js = json_dumps (contract_terms,
631 : JSON_INDENT (1));
632 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
633 : "Parser failed on %s:%u for input `%s'\n",
634 : error_name,
635 : error_line,
636 : js);
637 0 : free (js);
638 0 : TALER_TESTING_FAIL (is);
639 : }
640 34 : switch (version)
641 : {
642 28 : case 0:
643 : {
644 : struct GNUNET_JSON_Specification v0spec[] = {
645 28 : TALER_JSON_spec_amount_any ("amount",
646 : &ps->total_amount),
647 28 : TALER_JSON_spec_amount_any ("max_fee",
648 : &max_fee),
649 28 : GNUNET_JSON_spec_end ()
650 : };
651 :
652 28 : if (GNUNET_OK !=
653 28 : GNUNET_JSON_parse (contract_terms,
654 : v0spec,
655 : &error_name,
656 : &error_line))
657 : {
658 : char *js;
659 :
660 0 : js = json_dumps (contract_terms,
661 : JSON_INDENT (1));
662 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
663 : "Parser failed on %s:%u for input `%s'\n",
664 : error_name,
665 : error_line,
666 : js);
667 0 : free (js);
668 0 : TALER_TESTING_FAIL (is);
669 : }
670 : }
671 28 : if (0 < ps->choice_index)
672 0 : TALER_TESTING_FAIL (is);
673 28 : break;
674 6 : case 1:
675 : {
676 : const json_t *choices;
677 : const json_t *token_families;
678 : struct GNUNET_JSON_Specification v1spec[] = {
679 6 : GNUNET_JSON_spec_object_const ("token_families",
680 : &token_families),
681 6 : GNUNET_JSON_spec_array_const ("choices",
682 : &choices),
683 6 : GNUNET_JSON_spec_end ()
684 : };
685 : const json_t *outputs;
686 : json_t *output;
687 : unsigned int output_index;
688 : const json_t *choice;
689 :
690 6 : if (GNUNET_OK !=
691 6 : GNUNET_JSON_parse (contract_terms,
692 : v1spec,
693 : &error_name,
694 : &error_line))
695 : {
696 : char *js;
697 :
698 0 : js = json_dumps (contract_terms,
699 : JSON_INDENT (1));
700 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
701 : "Parser failed on %s:%u for input `%s'\n",
702 : error_name,
703 : error_line,
704 : js);
705 0 : free (js);
706 0 : TALER_TESTING_FAIL (is);
707 : }
708 :
709 6 : choice = json_array_get (choices,
710 6 : ps->choice_index);
711 6 : if (NULL == choice)
712 : {
713 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
714 : "No choice found at index %d\n",
715 : ps->choice_index);
716 0 : TALER_TESTING_FAIL (is);
717 : }
718 :
719 : {
720 6 : const char *ierror_name = NULL;
721 6 : unsigned int ierror_line = 0;
722 :
723 : struct GNUNET_JSON_Specification ispec[] = {
724 6 : TALER_JSON_spec_amount_any ("amount",
725 : &ps->total_amount),
726 6 : TALER_JSON_spec_amount_any ("max_fee",
727 : &max_fee),
728 6 : GNUNET_JSON_spec_array_const ("outputs",
729 : &outputs),
730 6 : GNUNET_JSON_spec_end ()
731 : };
732 :
733 6 : if (GNUNET_OK !=
734 6 : GNUNET_JSON_parse (choice,
735 : ispec,
736 : &ierror_name,
737 : &ierror_line))
738 : {
739 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
740 : "Parser failed on %s:%u for input `%s'\n",
741 : ierror_name,
742 : ierror_line,
743 : json_dumps (choice,
744 : JSON_INDENT (2)));
745 0 : TALER_TESTING_FAIL (is);
746 : }
747 : }
748 :
749 12 : json_array_foreach (outputs, output_index, output)
750 : {
751 : const char *slug;
752 : const char *kind;
753 : uint32_t key_index;
754 6 : uint32_t count = 1;
755 6 : const char *ierror_name = NULL;
756 6 : unsigned int ierror_line = 0;
757 :
758 : struct GNUNET_JSON_Specification ispec[] = {
759 6 : GNUNET_JSON_spec_string ("type",
760 : &kind),
761 6 : GNUNET_JSON_spec_string ("token_family_slug",
762 : &slug),
763 6 : GNUNET_JSON_spec_uint32 ("key_index",
764 : &key_index),
765 6 : GNUNET_JSON_spec_mark_optional (
766 : GNUNET_JSON_spec_uint32 ("count",
767 : &count),
768 : NULL),
769 6 : GNUNET_JSON_spec_end ()
770 : };
771 :
772 6 : if (GNUNET_OK !=
773 6 : GNUNET_JSON_parse (output,
774 : ispec,
775 : &ierror_name,
776 : &ierror_line))
777 : {
778 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
779 : "Parser failed on %s:%u for input `%s'\n",
780 : ierror_name,
781 : ierror_line,
782 : json_dumps (output,
783 : JSON_INDENT (2)));
784 0 : TALER_TESTING_FAIL (is);
785 : }
786 :
787 6 : if (0 != strcmp ("token", kind))
788 : {
789 0 : continue;
790 : }
791 :
792 6 : GNUNET_array_grow (ps->issued_tokens,
793 : ps->num_issued_tokens,
794 : ps->num_issued_tokens + count);
795 :
796 12 : for (unsigned int k = 0; k < count; k++)
797 : {
798 6 : struct TALER_MERCHANT_PrivateTokenDetails *details =
799 6 : &ps->issued_tokens[ps->num_issued_tokens - count + k];
800 :
801 6 : if (GNUNET_OK !=
802 6 : find_token_public_key (token_families,
803 : slug,
804 : key_index,
805 : &details->issue_pub))
806 : {
807 0 : TALER_TESTING_FAIL (is);
808 : }
809 :
810 : /* Only RSA is supported for now. */
811 6 : GNUNET_assert (GNUNET_CRYPTO_BSA_RSA ==
812 : details->issue_pub.public_key->cipher);
813 :
814 6 : TALER_token_blind_input_copy (&details->blinding_inputs,
815 : TALER_token_blind_input_rsa_singleton ()
816 : );
817 : /* FIXME: Where to get details->blinding_inputs from? */
818 6 : TALER_token_use_setup_random (&details->master);
819 6 : TALER_token_use_setup_priv (&details->master,
820 6 : &details->blinding_inputs,
821 : &details->token_priv);
822 6 : TALER_token_use_blinding_secret_create (&details->master,
823 6 : &details->blinding_inputs,
824 : &details->blinding_secret);
825 6 : GNUNET_CRYPTO_eddsa_key_get_public (&details->token_priv.private_key
826 : ,
827 : &details->token_pub.public_key);
828 6 : GNUNET_CRYPTO_hash (&details->token_pub.public_key,
829 : sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
830 : &details->h_token_pub.hash);
831 12 : details->envelope.blinded_pub = GNUNET_CRYPTO_message_blind_to_sign
832 : (
833 6 : details->issue_pub.public_key,
834 6 : &details->blinding_secret,
835 : NULL, /* FIXME: Add session nonce to support CS tokens */
836 6 : &details->h_token_pub.hash,
837 : sizeof (details->h_token_pub.hash),
838 6 : details->blinding_inputs.blinding_inputs);
839 :
840 6 : if (NULL == details->envelope.blinded_pub)
841 : {
842 0 : GNUNET_break (0);
843 0 : TALER_TESTING_FAIL (is);
844 : }
845 : }
846 : }
847 : }
848 :
849 6 : break;
850 0 : default:
851 0 : TALER_TESTING_FAIL (is);
852 : }
853 :
854 :
855 : }
856 :
857 : {
858 : char *cr;
859 :
860 34 : cr = GNUNET_strdup (ps->coin_reference);
861 34 : pay_coins = NULL;
862 34 : npay_coins = 0;
863 34 : if (GNUNET_OK !=
864 34 : build_coins (&pay_coins,
865 : &npay_coins,
866 : cr,
867 : is,
868 : ps->amount_with_fee,
869 : ps->amount_without_fee))
870 : {
871 0 : GNUNET_array_grow (pay_coins,
872 : npay_coins,
873 : 0);
874 0 : GNUNET_free (cr);
875 0 : TALER_TESTING_FAIL (is);
876 : }
877 34 : GNUNET_free (cr);
878 : }
879 34 : if (NULL != ps->token_reference)
880 : {
881 : char *tr;
882 :
883 4 : tr = GNUNET_strdup (ps->token_reference);
884 4 : if (GNUNET_OK !=
885 4 : build_tokens (&use_tokens,
886 : &len_use_tokens,
887 : tr,
888 : is))
889 : {
890 0 : GNUNET_array_grow (use_tokens,
891 : len_use_tokens,
892 : 0);
893 0 : GNUNET_free (tr);
894 0 : TALER_TESTING_FAIL (is);
895 : }
896 4 : GNUNET_free (tr);
897 : }
898 :
899 34 : GNUNET_array_grow (output_tokens,
900 : len_output_tokens,
901 : ps->num_issued_tokens);
902 40 : for (unsigned int i = 0; i<len_output_tokens; i++)
903 : {
904 6 : output_tokens[i].envelope.blinded_pub = ps->issued_tokens[i].envelope.
905 : blinded_pub;
906 : }
907 :
908 34 : if (GNUNET_OK !=
909 34 : TALER_TESTING_get_trait_merchant_sig (proposal_cmd,
910 : &merchant_sig))
911 0 : TALER_TESTING_FAIL (is);
912 :
913 34 : if (GNUNET_OK !=
914 34 : TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
915 : &h_proposal))
916 0 : TALER_TESTING_FAIL (is);
917 34 : ps->h_contract_terms = *h_proposal;
918 34 : ps->oph = TALER_MERCHANT_order_pay (
919 : TALER_TESTING_interpreter_get_context (is),
920 : ps->merchant_url,
921 : ps->session_id,
922 : h_proposal,
923 : ps->choice_index,
924 34 : &ps->total_amount,
925 : &max_fee,
926 : &merchant_pub,
927 : merchant_sig,
928 : timestamp,
929 : refund_deadline,
930 : pay_deadline,
931 : &h_wire,
932 : order_id,
933 : npay_coins,
934 : pay_coins,
935 : len_use_tokens,
936 : use_tokens,
937 : len_output_tokens,
938 : output_tokens,
939 : &pay_cb,
940 : ps);
941 34 : GNUNET_array_grow (pay_coins,
942 : npay_coins,
943 : 0);
944 34 : if (NULL == ps->oph)
945 0 : TALER_TESTING_FAIL (is);
946 : }
947 :
948 :
949 : /**
950 : * Free a "pay" CMD, and cancel it if need be.
951 : *
952 : * @param cls closure.
953 : * @param cmd command currently being freed.
954 : */
955 : static void
956 34 : pay_cleanup (void *cls,
957 : const struct TALER_TESTING_Command *cmd)
958 : {
959 34 : struct PayState *ps = cls;
960 :
961 34 : if (NULL != ps->oph)
962 : {
963 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
964 : "Command `%s' did not complete.\n",
965 : TALER_TESTING_interpreter_get_current_label (
966 : ps->is));
967 0 : TALER_MERCHANT_order_pay_cancel (ps->oph);
968 : }
969 :
970 34 : GNUNET_free (ps);
971 34 : }
972 :
973 :
974 : /**
975 : * Offer internal data useful to other commands.
976 : *
977 : * @param cls closure
978 : * @param[out] ret result
979 : * @param trait name of the trait
980 : * @param index index number of the object to extract.
981 : * @return #GNUNET_OK on success
982 : */
983 : static enum GNUNET_GenericReturnValue
984 24 : pay_traits (void *cls,
985 : const void **ret,
986 : const char *trait,
987 : unsigned int index)
988 : {
989 :
990 24 : struct PayState *ps = cls;
991 : const char *order_id;
992 : const struct TALER_TESTING_Command *proposal_cmd;
993 : const struct TALER_MerchantPublicKeyP *merchant_pub;
994 :
995 24 : if (NULL != ps->token_reference &&
996 0 : index >= ps->num_issued_tokens)
997 : {
998 0 : GNUNET_break (0);
999 0 : return GNUNET_NO;
1000 : }
1001 :
1002 24 : if (NULL ==
1003 : (proposal_cmd =
1004 24 : TALER_TESTING_interpreter_lookup_command (ps->is,
1005 : ps->proposal_reference)))
1006 : {
1007 0 : GNUNET_break (0);
1008 0 : return GNUNET_SYSERR;
1009 : }
1010 :
1011 24 : if (GNUNET_OK !=
1012 24 : TALER_TESTING_get_trait_order_id (proposal_cmd,
1013 : &order_id))
1014 : {
1015 0 : GNUNET_break (0);
1016 0 : return GNUNET_SYSERR;
1017 : }
1018 :
1019 24 : if (GNUNET_OK !=
1020 24 : TALER_TESTING_get_trait_merchant_pub (proposal_cmd,
1021 : &merchant_pub))
1022 : {
1023 0 : GNUNET_break (0);
1024 0 : return GNUNET_SYSERR;
1025 : }
1026 : {
1027 : struct TALER_Amount amount_with_fee;
1028 :
1029 24 : GNUNET_assert (GNUNET_OK ==
1030 : TALER_string_to_amount (ps->amount_with_fee,
1031 : &amount_with_fee));
1032 : {
1033 : struct TALER_TESTING_Trait traits[] = {
1034 24 : TALER_TESTING_make_trait_proposal_reference (ps->proposal_reference),
1035 24 : TALER_TESTING_make_trait_coin_reference (0,
1036 : ps->coin_reference),
1037 24 : TALER_TESTING_make_trait_order_id (order_id),
1038 24 : TALER_TESTING_make_trait_merchant_pub (merchant_pub),
1039 24 : TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig),
1040 24 : TALER_TESTING_make_trait_amount (&amount_with_fee),
1041 24 : TALER_TESTING_make_trait_otp_key (ps->pos_key),
1042 24 : TALER_TESTING_make_trait_otp_alg (&ps->pos_alg),
1043 24 : TALER_TESTING_make_trait_token_priv (index,
1044 24 : &ps->issued_tokens[index].
1045 : token_priv),
1046 24 : TALER_TESTING_make_trait_token_issue_pub (index,
1047 24 : &ps->issued_tokens[index].
1048 : issue_pub),
1049 24 : TALER_TESTING_make_trait_token_issue_sig (index,
1050 24 : &ps->issued_tokens[index].
1051 : issue_sig),
1052 24 : TALER_TESTING_trait_end ()
1053 : };
1054 :
1055 24 : return TALER_TESTING_get_trait (traits,
1056 : ret,
1057 : trait,
1058 : index);
1059 : }
1060 : }
1061 : }
1062 :
1063 :
1064 : struct TALER_TESTING_Command
1065 34 : TALER_TESTING_cmd_merchant_pay_order_choices (const char *label,
1066 : const char *merchant_url,
1067 : unsigned int http_status,
1068 : const char *proposal_reference,
1069 : const char *coin_reference,
1070 : const char *amount_with_fee,
1071 : const char *amount_without_fee,
1072 : const char *session_id,
1073 : int choice_index,
1074 : const char *token_reference)
1075 : {
1076 : struct PayState *ps;
1077 :
1078 34 : ps = GNUNET_new (struct PayState);
1079 34 : ps->http_status = http_status;
1080 34 : ps->proposal_reference = proposal_reference;
1081 34 : ps->coin_reference = coin_reference;
1082 34 : ps->merchant_url = merchant_url;
1083 34 : ps->amount_with_fee = amount_with_fee;
1084 34 : ps->amount_without_fee = amount_without_fee;
1085 34 : ps->session_id = session_id;
1086 34 : ps->token_reference = token_reference;
1087 34 : ps->choice_index = choice_index;
1088 : {
1089 34 : struct TALER_TESTING_Command cmd = {
1090 : .cls = ps,
1091 : .label = label,
1092 : .run = &pay_run,
1093 : .cleanup = &pay_cleanup,
1094 : .traits = &pay_traits
1095 : };
1096 :
1097 34 : return cmd;
1098 : }
1099 : }
1100 :
1101 :
1102 : struct TALER_TESTING_Command
1103 28 : TALER_TESTING_cmd_merchant_pay_order (const char *label,
1104 : const char *merchant_url,
1105 : unsigned int http_status,
1106 : const char *proposal_reference,
1107 : const char *coin_reference,
1108 : const char *amount_with_fee,
1109 : const char *amount_without_fee,
1110 : const char *session_id)
1111 : {
1112 28 : return TALER_TESTING_cmd_merchant_pay_order_choices (label,
1113 : merchant_url,
1114 : http_status,
1115 : proposal_reference,
1116 : coin_reference,
1117 : amount_with_fee,
1118 : amount_without_fee,
1119 : session_id,
1120 : -1,
1121 : NULL);
1122 : }
1123 :
1124 :
1125 : /* end of testing_api_cmd_pay_order.c */
|