Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018-2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it
6 : under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3, or (at your
8 : 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
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_withdraw.c
21 : * @brief main interpreter loop for testcases
22 : * @author Christian Grothoff
23 : * @author Marcello Stanisci
24 : * @author Özgür Kesim
25 : */
26 : #include "taler/platform.h"
27 : #include "taler/taler_json_lib.h"
28 : #include <microhttpd.h>
29 : #include <gnunet/gnunet_curl_lib.h>
30 : #include "taler/taler_signatures.h"
31 : #include "taler/taler_extensions.h"
32 : #include "taler/taler_testing_lib.h"
33 : #include "taler/backoff.h"
34 :
35 :
36 : /**
37 : * How often do we retry before giving up?
38 : */
39 : #define NUM_RETRIES 15
40 :
41 : /**
42 : * How long do we wait AT LEAST if the exchange says the reserve is unknown?
43 : */
44 : #define UNKNOWN_MIN_BACKOFF GNUNET_TIME_relative_multiply ( \
45 : GNUNET_TIME_UNIT_MILLISECONDS, 10)
46 :
47 : /**
48 : * How long do we wait AT MOST if the exchange says the reserve is unknown?
49 : */
50 : #define UNKNOWN_MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
51 : GNUNET_TIME_UNIT_MILLISECONDS, 100)
52 :
53 : /**
54 : * State for a "withdraw" CMD.
55 : */
56 : struct WithdrawState
57 : {
58 :
59 : /**
60 : * Which reserve should we withdraw from?
61 : */
62 : const char *reserve_reference;
63 :
64 : /**
65 : * Reference to a withdraw or reveal operation from which we should
66 : * reuse the private coin key, or NULL for regular withdrawal.
67 : */
68 : const char *reuse_coin_key_ref;
69 :
70 : /**
71 : * If true and @e reuse_coin_key_ref is not NULL, also reuses
72 : * the blinding_seed.
73 : */
74 : bool reuse_blinding_seed;
75 :
76 : /**
77 : * Our command.
78 : */
79 : const struct TALER_TESTING_Command *cmd;
80 :
81 : /**
82 : * String describing the denomination value we should withdraw.
83 : * A corresponding denomination key must exist in the exchange's
84 : * offerings. Can be NULL if @e pk is set instead.
85 : */
86 : struct TALER_Amount amount;
87 :
88 : /**
89 : * If @e amount is NULL, this specifies the denomination key to
90 : * use. Otherwise, this will be set (by the interpreter) to the
91 : * denomination PK matching @e amount.
92 : */
93 : struct TALER_EXCHANGE_DenomPublicKey *pk;
94 :
95 : /**
96 : * Exchange base URL. Only used as offered trait.
97 : */
98 : char *exchange_url;
99 :
100 : /**
101 : * URI if the reserve we are withdrawing from.
102 : */
103 : struct TALER_NormalizedPayto reserve_payto_uri;
104 :
105 : /**
106 : * Private key of the reserve we are withdrawing from.
107 : */
108 : struct TALER_ReservePrivateKeyP reserve_priv;
109 :
110 : /**
111 : * Public key of the reserve we are withdrawing from.
112 : */
113 : struct TALER_ReservePublicKeyP reserve_pub;
114 :
115 : /**
116 : * Private key of the coin.
117 : */
118 : struct TALER_CoinSpendPrivateKeyP coin_priv;
119 :
120 : /**
121 : * Public key of the coin.
122 : */
123 : struct TALER_CoinSpendPublicKeyP coin_pub;
124 :
125 : /**
126 : * Blinding key used during the operation.
127 : */
128 : union GNUNET_CRYPTO_BlindingSecretP bks;
129 :
130 : /**
131 : * Values contributed from the exchange during the
132 : * withdraw protocol.
133 : */
134 : struct TALER_ExchangeBlindingValues exchange_vals;
135 :
136 : /**
137 : * Interpreter state (during command).
138 : */
139 : struct TALER_TESTING_Interpreter *is;
140 :
141 : /**
142 : * Set (by the interpreter) to the exchange's signature over the
143 : * coin's public key.
144 : */
145 : struct TALER_DenominationSignature sig;
146 :
147 : /**
148 : * Seed for the key material of the coin, set by the interpreter.
149 : */
150 : struct TALER_WithdrawMasterSeedP seed;
151 :
152 : /**
153 : * Blinding seed for the blinding preparation for CS.
154 : */
155 : struct TALER_BlindingMasterSeedP blinding_seed;
156 :
157 : /**
158 : * An age > 0 signifies age restriction is required
159 : */
160 : uint8_t age;
161 :
162 : /**
163 : * If age > 0, put here the corresponding age commitment with its proof and
164 : * its hash, respectively.
165 : */
166 : struct TALER_AgeCommitmentProof age_commitment_proof;
167 : struct TALER_AgeCommitmentHashP h_age_commitment;
168 :
169 : /**
170 : * Reserve history entry that corresponds to this operation.
171 : * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
172 : */
173 : struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
174 :
175 : /**
176 : * Withdraw handle (while operation is running).
177 : */
178 : struct TALER_EXCHANGE_PostWithdrawHandle *wsh;
179 :
180 : /**
181 : * The commitment for the withdraw operation, later needed for /recoup
182 : */
183 : struct TALER_HashBlindedPlanchetsP planchets_h;
184 :
185 : /**
186 : * Task scheduled to try later.
187 : */
188 : struct GNUNET_SCHEDULER_Task *retry_task;
189 :
190 : /**
191 : * How long do we wait until we retry?
192 : */
193 : struct GNUNET_TIME_Relative backoff;
194 :
195 : /**
196 : * Total withdraw backoff applied.
197 : */
198 : struct GNUNET_TIME_Relative total_backoff;
199 :
200 : /**
201 : * Set to the KYC requirement payto hash *if* the exchange replied with a
202 : * request for KYC.
203 : */
204 : struct TALER_NormalizedPaytoHashP h_payto;
205 :
206 : /**
207 : * Set to the KYC requirement row *if* the exchange replied with
208 : * a request for KYC.
209 : */
210 : uint64_t requirement_row;
211 :
212 : /**
213 : * Expected HTTP response code to the request.
214 : */
215 : unsigned int expected_response_code;
216 :
217 : /**
218 : * Was this command modified via
219 : * #TALER_TESTING_cmd_withdraw_with_retry to
220 : * enable retries? How often should we still retry?
221 : */
222 : unsigned int do_retry;
223 : };
224 :
225 :
226 : /**
227 : * Run the command.
228 : *
229 : * @param cls closure.
230 : * @param cmd the commaind being run.
231 : * @param is interpreter state.
232 : */
233 : static void
234 : withdraw_run (void *cls,
235 : const struct TALER_TESTING_Command *cmd,
236 : struct TALER_TESTING_Interpreter *is);
237 :
238 :
239 : /**
240 : * Task scheduled to re-try #withdraw_run.
241 : *
242 : * @param cls a `struct WithdrawState`
243 : */
244 : static void
245 0 : do_retry (void *cls)
246 : {
247 0 : struct WithdrawState *ws = cls;
248 :
249 0 : ws->retry_task = NULL;
250 0 : TALER_TESTING_touch_cmd (ws->is);
251 0 : withdraw_run (ws,
252 : NULL,
253 : ws->is);
254 0 : }
255 :
256 :
257 : /**
258 : * "reserve withdraw" operation callback; checks that the
259 : * response code is expected and store the exchange signature
260 : * in the state.
261 : *
262 : * @param cls closure.
263 : * @param wr withdraw response details
264 : */
265 : static void
266 68 : withdraw_cb (void *cls,
267 : const struct TALER_EXCHANGE_PostWithdrawResponse *wr)
268 : {
269 68 : struct WithdrawState *ws = cls;
270 68 : struct TALER_TESTING_Interpreter *is = ws->is;
271 :
272 68 : ws->wsh = NULL;
273 68 : if (ws->expected_response_code != wr->hr.http_status)
274 : {
275 0 : if (0 != ws->do_retry)
276 : {
277 0 : if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec)
278 0 : ws->do_retry--; /* we don't count reserve unknown as failures here */
279 0 : if ( (0 == wr->hr.http_status) ||
280 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) ||
281 0 : (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == wr->hr.ec) ||
282 0 : (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN == wr->hr.ec) ||
283 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == wr->hr.http_status) )
284 : {
285 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286 : "Retrying withdraw failed with %u/%d\n",
287 : wr->hr.http_status,
288 : (int) wr->hr.ec);
289 : /* on DB conflicts, do not use backoff */
290 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec)
291 0 : ws->backoff = GNUNET_TIME_UNIT_ZERO;
292 0 : else if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec)
293 0 : ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff);
294 : else
295 0 : ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF,
296 : ws->backoff);
297 0 : ws->backoff = GNUNET_TIME_relative_min (ws->backoff,
298 : UNKNOWN_MAX_BACKOFF);
299 0 : ws->total_backoff = GNUNET_TIME_relative_add (ws->total_backoff,
300 : ws->backoff);
301 0 : TALER_TESTING_inc_tries (ws->is);
302 0 : ws->retry_task = GNUNET_SCHEDULER_add_delayed (ws->backoff,
303 : &do_retry,
304 : ws);
305 0 : return;
306 : }
307 : }
308 0 : TALER_TESTING_unexpected_status_with_body (is,
309 : wr->hr.http_status,
310 : ws->expected_response_code,
311 : wr->hr.reply);
312 0 : return;
313 : }
314 68 : switch (wr->hr.http_status)
315 : {
316 59 : case MHD_HTTP_OK:
317 59 : GNUNET_assert (1 == wr->details.ok.num_sigs);
318 59 : TALER_denom_sig_copy (&ws->sig,
319 59 : &wr->details.ok.coin_details[0].denom_sig);
320 59 : ws->coin_priv = wr->details.ok.coin_details[0].coin_priv;
321 59 : GNUNET_CRYPTO_eddsa_key_get_public (&ws->coin_priv.eddsa_priv,
322 : &ws->coin_pub.eddsa_pub);
323 59 : ws->bks = wr->details.ok.coin_details[0].blinding_key;
324 59 : TALER_denom_ewv_copy (&ws->exchange_vals,
325 59 : &wr->details.ok.coin_details[0].blinding_values);
326 59 : ws->planchets_h = wr->details.ok.planchets_h;
327 59 : if (0<ws->age)
328 : {
329 : /* copy the age-commitment data */
330 10 : ws->h_age_commitment = wr->details.ok.coin_details[0].h_age_commitment;
331 10 : TALER_age_commitment_proof_deep_copy (
332 : &ws->age_commitment_proof,
333 10 : &wr->details.ok.coin_details[0].age_commitment_proof);
334 : }
335 :
336 59 : if (0 != ws->total_backoff.rel_value_us)
337 : {
338 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
339 : "Total withdraw backoff for %s was %s\n",
340 : ws->cmd->label,
341 : GNUNET_STRINGS_relative_time_to_string (ws->total_backoff,
342 : true));
343 : }
344 59 : break;
345 0 : case MHD_HTTP_FORBIDDEN:
346 : /* nothing to check */
347 0 : break;
348 0 : case MHD_HTTP_NOT_FOUND:
349 : /* nothing to check */
350 0 : break;
351 5 : case MHD_HTTP_CONFLICT:
352 : /* nothing to check */
353 5 : break;
354 0 : case MHD_HTTP_GONE:
355 : /* theoretically could check that the key was actually */
356 0 : break;
357 4 : case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
358 : /* KYC required */
359 4 : ws->requirement_row =
360 4 : wr->details.unavailable_for_legal_reasons.requirement_row;
361 : ws->h_payto
362 4 : = wr->details.unavailable_for_legal_reasons.h_payto;
363 4 : break;
364 0 : default:
365 : /* Unsupported status code (by test harness) */
366 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
367 : "Withdraw test command does not support status code %u\n",
368 : wr->hr.http_status);
369 0 : GNUNET_break (0);
370 0 : break;
371 : }
372 68 : TALER_TESTING_interpreter_next (is);
373 : }
374 :
375 :
376 : /**
377 : * Run the command.
378 : */
379 : static void
380 68 : withdraw_run (void *cls,
381 : const struct TALER_TESTING_Command *cmd,
382 : struct TALER_TESTING_Interpreter *is)
383 : {
384 68 : struct WithdrawState *ws = cls;
385 : const struct TALER_ReservePrivateKeyP *rp;
386 : const struct TALER_TESTING_Command *create_reserve;
387 : const struct TALER_EXCHANGE_DenomPublicKey *dpk;
388 :
389 68 : if (NULL != cmd)
390 68 : ws->cmd = cmd;
391 68 : ws->is = is;
392 : create_reserve
393 68 : = TALER_TESTING_interpreter_lookup_command (
394 : is,
395 : ws->reserve_reference);
396 68 : if (NULL == create_reserve)
397 : {
398 0 : GNUNET_break (0);
399 0 : TALER_TESTING_interpreter_fail (is);
400 0 : return;
401 : }
402 68 : if (GNUNET_OK !=
403 68 : TALER_TESTING_get_trait_reserve_priv (create_reserve,
404 : &rp))
405 : {
406 0 : GNUNET_break (0);
407 0 : TALER_TESTING_interpreter_fail (is);
408 0 : return;
409 : }
410 68 : if (NULL == ws->exchange_url)
411 : ws->exchange_url
412 68 : = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
413 68 : ws->reserve_priv = *rp;
414 68 : GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
415 : &ws->reserve_pub.eddsa_pub);
416 : ws->reserve_payto_uri
417 68 : = TALER_reserve_make_payto (ws->exchange_url,
418 68 : &ws->reserve_pub);
419 :
420 68 : TALER_withdraw_master_seed_setup_random (&ws->seed);
421 68 : TALER_cs_withdraw_seed_to_blinding_seed (&ws->seed,
422 : &ws->blinding_seed);
423 :
424 : /**
425 : * In case of coin key material reuse, we _only_ reuse the
426 : * master seed, but the blinding seed is still randomly chosen,
427 : * see the lines prior to this.
428 : */
429 68 : if (NULL != ws->reuse_coin_key_ref)
430 : {
431 : const struct TALER_WithdrawMasterSeedP *seed;
432 : const struct TALER_TESTING_Command *cref;
433 : char *cstr;
434 : unsigned int index;
435 :
436 6 : GNUNET_assert (GNUNET_OK ==
437 : TALER_TESTING_parse_coin_reference (
438 : ws->reuse_coin_key_ref,
439 : &cstr,
440 : &index));
441 6 : cref = TALER_TESTING_interpreter_lookup_command (is,
442 : cstr);
443 6 : GNUNET_assert (NULL != cref);
444 6 : GNUNET_free (cstr);
445 6 : GNUNET_assert (GNUNET_OK ==
446 : TALER_TESTING_get_trait_withdraw_seed (cref,
447 : &seed));
448 6 : ws->seed = *seed;
449 :
450 6 : if (ws->reuse_blinding_seed)
451 2 : TALER_cs_withdraw_seed_to_blinding_seed (&ws->seed,
452 : &ws->blinding_seed);
453 : }
454 :
455 68 : if (NULL == ws->pk)
456 : {
457 68 : dpk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (is),
458 68 : &ws->amount,
459 68 : ws->age > 0);
460 68 : if (NULL == dpk)
461 : {
462 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463 : "Failed to determine denomination key at %s\n",
464 : (NULL != cmd) ? cmd->label : "<retried command>");
465 0 : GNUNET_break (0);
466 0 : TALER_TESTING_interpreter_fail (is);
467 0 : return;
468 : }
469 : /* We copy the denomination key, as re-querying /keys
470 : * would free the old one. */
471 68 : ws->pk = TALER_EXCHANGE_copy_denomination_key (dpk);
472 : }
473 : else
474 : {
475 0 : ws->amount = ws->pk->value;
476 : }
477 :
478 68 : ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
479 68 : GNUNET_assert (0 <=
480 : TALER_amount_add (&ws->reserve_history.amount,
481 : &ws->amount,
482 : &ws->pk->fees.withdraw));
483 68 : ws->reserve_history.details.withdraw.fee =
484 68 : ws->pk->fees.withdraw;
485 :
486 68 : ws->wsh = TALER_EXCHANGE_post_withdraw_create (
487 : TALER_TESTING_interpreter_get_context (is),
488 : TALER_TESTING_get_exchange_url (is),
489 : TALER_TESTING_get_keys (is),
490 : rp,
491 : 1,
492 68 : ws->pk,
493 68 : &ws->seed,
494 68 : ws->age);
495 68 : if (NULL == ws->wsh)
496 : {
497 0 : GNUNET_break (0);
498 0 : TALER_TESTING_interpreter_fail (is);
499 0 : return;
500 : }
501 68 : GNUNET_assert (GNUNET_OK ==
502 : TALER_EXCHANGE_post_withdraw_set_options (
503 : ws->wsh,
504 : TALER_EXCHANGE_post_withdraw_option_blinding_seed (
505 : &ws->blinding_seed)));
506 68 : GNUNET_assert (TALER_EC_NONE ==
507 : TALER_EXCHANGE_post_withdraw_start (ws->wsh,
508 : &withdraw_cb,
509 : ws));
510 : }
511 :
512 :
513 : /**
514 : * Free the state of a "withdraw" CMD, and possibly cancel
515 : * a pending operation thereof.
516 : *
517 : * @param cls closure.
518 : * @param cmd the command being freed.
519 : */
520 : static void
521 68 : withdraw_cleanup (void *cls,
522 : const struct TALER_TESTING_Command *cmd)
523 : {
524 68 : struct WithdrawState *ws = cls;
525 :
526 68 : if (NULL != ws->wsh)
527 : {
528 0 : TALER_TESTING_command_incomplete (ws->is,
529 : cmd->label);
530 0 : TALER_EXCHANGE_post_withdraw_cancel (ws->wsh);
531 0 : ws->wsh = NULL;
532 : }
533 68 : if (NULL != ws->retry_task)
534 : {
535 0 : GNUNET_SCHEDULER_cancel (ws->retry_task);
536 0 : ws->retry_task = NULL;
537 : }
538 68 : TALER_denom_sig_free (&ws->sig);
539 68 : TALER_denom_ewv_free (&ws->exchange_vals);
540 68 : if (NULL != ws->pk)
541 : {
542 68 : TALER_EXCHANGE_destroy_denomination_key (ws->pk);
543 68 : ws->pk = NULL;
544 : }
545 68 : if (ws->age > 0)
546 10 : TALER_age_commitment_proof_free (&ws->age_commitment_proof);
547 68 : GNUNET_free (ws->exchange_url);
548 68 : GNUNET_free (ws->reserve_payto_uri.normalized_payto);
549 68 : GNUNET_free (ws);
550 68 : }
551 :
552 :
553 : /**
554 : * Offer internal data to a "withdraw" CMD state to other
555 : * commands.
556 : *
557 : * @param cls closure
558 : * @param[out] ret result (could be anything)
559 : * @param trait name of the trait
560 : * @param index index number of the object to offer.
561 : * @return #GNUNET_OK on success
562 : */
563 : static enum GNUNET_GenericReturnValue
564 1825 : withdraw_traits (void *cls,
565 : const void **ret,
566 : const char *trait,
567 : unsigned int index)
568 : {
569 1825 : struct WithdrawState *ws = cls;
570 : struct TALER_TESTING_Trait traits[] = {
571 : /* history entry MUST be first due to response code logic below! */
572 1825 : TALER_TESTING_make_trait_reserve_history (0 /* only one coin */,
573 1825 : &ws->reserve_history),
574 1825 : TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
575 1825 : &ws->coin_priv),
576 1825 : TALER_TESTING_make_trait_coin_pub (0 /* only one coin */,
577 1825 : &ws->coin_pub),
578 1825 : TALER_TESTING_make_trait_withdraw_seed (&ws->seed),
579 1825 : TALER_TESTING_make_trait_blinding_seed (&ws->blinding_seed),
580 1825 : TALER_TESTING_make_trait_withdraw_commitment (&ws->planchets_h),
581 1825 : TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
582 1825 : &ws->bks),
583 1825 : TALER_TESTING_make_trait_exchange_blinding_values (0 /* only one coin */,
584 1825 : &ws->exchange_vals),
585 1825 : TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
586 1825 : ws->pk),
587 1825 : TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
588 1825 : &ws->sig),
589 1825 : TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv),
590 1825 : TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub),
591 1825 : TALER_TESTING_make_trait_amount (&ws->amount),
592 1825 : TALER_TESTING_make_trait_legi_requirement_row (&ws->requirement_row),
593 1825 : TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto),
594 1825 : TALER_TESTING_make_trait_normalized_payto_uri (&ws->reserve_payto_uri),
595 1825 : TALER_TESTING_make_trait_exchange_url (ws->exchange_url),
596 1825 : TALER_TESTING_make_trait_age_commitment_proof (0,
597 1825 : 0 < ws->age
598 : ? &ws->age_commitment_proof
599 : : NULL),
600 1825 : TALER_TESTING_make_trait_h_age_commitment (0,
601 1825 : 0 < ws->age
602 : ? &ws->h_age_commitment
603 : : NULL),
604 1825 : TALER_TESTING_trait_end ()
605 : };
606 :
607 1825 : return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK)
608 : ? &traits[0] /* we have reserve history */
609 : : &traits[1], /* skip reserve history */
610 : ret,
611 : trait,
612 : index);
613 : }
614 :
615 :
616 : struct TALER_TESTING_Command
617 68 : TALER_TESTING_cmd_withdraw_amount (const char *label,
618 : const char *reserve_reference,
619 : const char *amount,
620 : uint8_t age,
621 : unsigned int expected_response_code)
622 : {
623 : struct WithdrawState *ws;
624 :
625 68 : ws = GNUNET_new (struct WithdrawState);
626 68 : ws->age = age;
627 68 : ws->reserve_reference = reserve_reference;
628 68 : if (GNUNET_OK !=
629 68 : TALER_string_to_amount (amount,
630 : &ws->amount))
631 : {
632 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
633 : "Failed to parse amount `%s' at %s\n",
634 : amount,
635 : label);
636 0 : GNUNET_assert (0);
637 : }
638 68 : ws->expected_response_code = expected_response_code;
639 : {
640 68 : struct TALER_TESTING_Command cmd = {
641 : .cls = ws,
642 : .label = label,
643 : .run = &withdraw_run,
644 : .cleanup = &withdraw_cleanup,
645 : .traits = &withdraw_traits
646 : };
647 :
648 68 : return cmd;
649 : }
650 : }
651 :
652 :
653 : struct TALER_TESTING_Command
654 4 : TALER_TESTING_cmd_withdraw_amount_reuse_key (
655 : const char *label,
656 : const char *reserve_reference,
657 : const char *amount,
658 : uint8_t age,
659 : const char *coin_ref,
660 : unsigned int expected_response_code)
661 : {
662 : struct TALER_TESTING_Command cmd;
663 :
664 4 : cmd = TALER_TESTING_cmd_withdraw_amount (label,
665 : reserve_reference,
666 : amount,
667 : age,
668 : expected_response_code);
669 : {
670 4 : struct WithdrawState *ws = cmd.cls;
671 :
672 4 : ws->reuse_coin_key_ref = coin_ref;
673 : }
674 4 : return cmd;
675 : }
676 :
677 :
678 : struct TALER_TESTING_Command
679 2 : TALER_TESTING_cmd_withdraw_amount_reuse_all_secrets (
680 : const char *label,
681 : const char *reserve_reference,
682 : const char *amount,
683 : uint8_t age,
684 : const char *coin_ref,
685 : unsigned int expected_response_code)
686 : {
687 : struct TALER_TESTING_Command cmd;
688 :
689 2 : cmd = TALER_TESTING_cmd_withdraw_amount (label,
690 : reserve_reference,
691 : amount,
692 : age,
693 : expected_response_code);
694 : {
695 2 : struct WithdrawState *ws = cmd.cls;
696 :
697 2 : ws->reuse_coin_key_ref = coin_ref;
698 2 : ws->reuse_blinding_seed = true;
699 : }
700 2 : return cmd;
701 : }
702 :
703 :
704 : struct TALER_TESTING_Command
705 0 : TALER_TESTING_cmd_withdraw_denomination (
706 : const char *label,
707 : const char *reserve_reference,
708 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
709 : unsigned int expected_response_code)
710 : {
711 : struct WithdrawState *ws;
712 :
713 0 : if (NULL == dk)
714 : {
715 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
716 : "Denomination key not specified at %s\n",
717 : label);
718 0 : GNUNET_assert (0);
719 : }
720 0 : ws = GNUNET_new (struct WithdrawState);
721 0 : ws->reserve_reference = reserve_reference;
722 0 : ws->pk = TALER_EXCHANGE_copy_denomination_key (dk);
723 0 : ws->expected_response_code = expected_response_code;
724 : {
725 0 : struct TALER_TESTING_Command cmd = {
726 : .cls = ws,
727 : .label = label,
728 : .run = &withdraw_run,
729 : .cleanup = &withdraw_cleanup,
730 : .traits = &withdraw_traits
731 : };
732 :
733 0 : return cmd;
734 : }
735 : }
736 :
737 :
738 : struct TALER_TESTING_Command
739 0 : TALER_TESTING_cmd_withdraw_with_retry (struct TALER_TESTING_Command cmd)
740 : {
741 : struct WithdrawState *ws;
742 :
743 0 : GNUNET_assert (&withdraw_run == cmd.run);
744 0 : ws = cmd.cls;
745 0 : ws->do_retry = NUM_RETRIES;
746 0 : return cmd;
747 : }
748 :
749 :
750 : /* end of testing_api_cmd_withdraw.c */
|