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