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_WithdrawHandle *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_WithdrawResponse *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_withdraw_extra_blinding_seed (
487 : TALER_TESTING_interpreter_get_context (is),
488 : TALER_TESTING_get_keys (is),
489 : TALER_TESTING_get_exchange_url (is),
490 : rp,
491 : 1,
492 68 : ws->pk,
493 68 : &ws->seed,
494 68 : &ws->blinding_seed,
495 68 : ws->age,
496 : &withdraw_cb,
497 : ws);
498 :
499 68 : if (NULL == ws->wsh)
500 : {
501 0 : GNUNET_break (0);
502 0 : TALER_TESTING_interpreter_fail (is);
503 0 : return;
504 : }
505 : }
506 :
507 :
508 : /**
509 : * Free the state of a "withdraw" CMD, and possibly cancel
510 : * a pending operation thereof.
511 : *
512 : * @param cls closure.
513 : * @param cmd the command being freed.
514 : */
515 : static void
516 68 : withdraw_cleanup (void *cls,
517 : const struct TALER_TESTING_Command *cmd)
518 : {
519 68 : struct WithdrawState *ws = cls;
520 :
521 68 : if (NULL != ws->wsh)
522 : {
523 0 : TALER_TESTING_command_incomplete (ws->is,
524 : cmd->label);
525 0 : TALER_EXCHANGE_withdraw_cancel (ws->wsh);
526 0 : ws->wsh = NULL;
527 : }
528 68 : if (NULL != ws->retry_task)
529 : {
530 0 : GNUNET_SCHEDULER_cancel (ws->retry_task);
531 0 : ws->retry_task = NULL;
532 : }
533 68 : TALER_denom_sig_free (&ws->sig);
534 68 : TALER_denom_ewv_free (&ws->exchange_vals);
535 68 : if (NULL != ws->pk)
536 : {
537 68 : TALER_EXCHANGE_destroy_denomination_key (ws->pk);
538 68 : ws->pk = NULL;
539 : }
540 68 : if (ws->age > 0)
541 10 : TALER_age_commitment_proof_free (&ws->age_commitment_proof);
542 68 : GNUNET_free (ws->exchange_url);
543 68 : GNUNET_free (ws->reserve_payto_uri.normalized_payto);
544 68 : GNUNET_free (ws);
545 68 : }
546 :
547 :
548 : /**
549 : * Offer internal data to a "withdraw" CMD state to other
550 : * commands.
551 : *
552 : * @param cls closure
553 : * @param[out] ret result (could be anything)
554 : * @param trait name of the trait
555 : * @param index index number of the object to offer.
556 : * @return #GNUNET_OK on success
557 : */
558 : static enum GNUNET_GenericReturnValue
559 1825 : withdraw_traits (void *cls,
560 : const void **ret,
561 : const char *trait,
562 : unsigned int index)
563 : {
564 1825 : struct WithdrawState *ws = cls;
565 : struct TALER_TESTING_Trait traits[] = {
566 : /* history entry MUST be first due to response code logic below! */
567 1825 : TALER_TESTING_make_trait_reserve_history (0 /* only one coin */,
568 1825 : &ws->reserve_history),
569 1825 : TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
570 1825 : &ws->coin_priv),
571 1825 : TALER_TESTING_make_trait_coin_pub (0 /* only one coin */,
572 1825 : &ws->coin_pub),
573 1825 : TALER_TESTING_make_trait_withdraw_seed (&ws->seed),
574 1825 : TALER_TESTING_make_trait_blinding_seed (&ws->blinding_seed),
575 1825 : TALER_TESTING_make_trait_withdraw_commitment (&ws->planchets_h),
576 1825 : TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
577 1825 : &ws->bks),
578 1825 : TALER_TESTING_make_trait_exchange_blinding_values (0 /* only one coin */,
579 1825 : &ws->exchange_vals),
580 1825 : TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
581 1825 : ws->pk),
582 1825 : TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
583 1825 : &ws->sig),
584 1825 : TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv),
585 1825 : TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub),
586 1825 : TALER_TESTING_make_trait_amount (&ws->amount),
587 1825 : TALER_TESTING_make_trait_legi_requirement_row (&ws->requirement_row),
588 1825 : TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto),
589 1825 : TALER_TESTING_make_trait_normalized_payto_uri (&ws->reserve_payto_uri),
590 1825 : TALER_TESTING_make_trait_exchange_url (ws->exchange_url),
591 1825 : TALER_TESTING_make_trait_age_commitment_proof (0,
592 1825 : 0 < ws->age
593 : ? &ws->age_commitment_proof
594 : : NULL),
595 1825 : TALER_TESTING_make_trait_h_age_commitment (0,
596 1825 : 0 < ws->age
597 : ? &ws->h_age_commitment
598 : : NULL),
599 1825 : TALER_TESTING_trait_end ()
600 : };
601 :
602 1825 : return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK)
603 : ? &traits[0] /* we have reserve history */
604 : : &traits[1], /* skip reserve history */
605 : ret,
606 : trait,
607 : index);
608 : }
609 :
610 :
611 : struct TALER_TESTING_Command
612 68 : TALER_TESTING_cmd_withdraw_amount (const char *label,
613 : const char *reserve_reference,
614 : const char *amount,
615 : uint8_t age,
616 : unsigned int expected_response_code)
617 : {
618 : struct WithdrawState *ws;
619 :
620 68 : ws = GNUNET_new (struct WithdrawState);
621 68 : ws->age = age;
622 68 : ws->reserve_reference = reserve_reference;
623 68 : if (GNUNET_OK !=
624 68 : TALER_string_to_amount (amount,
625 : &ws->amount))
626 : {
627 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
628 : "Failed to parse amount `%s' at %s\n",
629 : amount,
630 : label);
631 0 : GNUNET_assert (0);
632 : }
633 68 : ws->expected_response_code = expected_response_code;
634 : {
635 68 : struct TALER_TESTING_Command cmd = {
636 : .cls = ws,
637 : .label = label,
638 : .run = &withdraw_run,
639 : .cleanup = &withdraw_cleanup,
640 : .traits = &withdraw_traits
641 : };
642 :
643 68 : return cmd;
644 : }
645 : }
646 :
647 :
648 : struct TALER_TESTING_Command
649 4 : TALER_TESTING_cmd_withdraw_amount_reuse_key (
650 : const char *label,
651 : const char *reserve_reference,
652 : const char *amount,
653 : uint8_t age,
654 : const char *coin_ref,
655 : unsigned int expected_response_code)
656 : {
657 : struct TALER_TESTING_Command cmd;
658 :
659 4 : cmd = TALER_TESTING_cmd_withdraw_amount (label,
660 : reserve_reference,
661 : amount,
662 : age,
663 : expected_response_code);
664 : {
665 4 : struct WithdrawState *ws = cmd.cls;
666 :
667 4 : ws->reuse_coin_key_ref = coin_ref;
668 : }
669 4 : return cmd;
670 : }
671 :
672 :
673 : struct TALER_TESTING_Command
674 2 : TALER_TESTING_cmd_withdraw_amount_reuse_all_secrets (
675 : const char *label,
676 : const char *reserve_reference,
677 : const char *amount,
678 : uint8_t age,
679 : const char *coin_ref,
680 : unsigned int expected_response_code)
681 : {
682 : struct TALER_TESTING_Command cmd;
683 :
684 2 : cmd = TALER_TESTING_cmd_withdraw_amount (label,
685 : reserve_reference,
686 : amount,
687 : age,
688 : expected_response_code);
689 : {
690 2 : struct WithdrawState *ws = cmd.cls;
691 :
692 2 : ws->reuse_coin_key_ref = coin_ref;
693 2 : ws->reuse_blinding_seed = true;
694 : }
695 2 : return cmd;
696 : }
697 :
698 :
699 : struct TALER_TESTING_Command
700 0 : TALER_TESTING_cmd_withdraw_denomination (
701 : const char *label,
702 : const char *reserve_reference,
703 : const struct TALER_EXCHANGE_DenomPublicKey *dk,
704 : unsigned int expected_response_code)
705 : {
706 : struct WithdrawState *ws;
707 :
708 0 : if (NULL == dk)
709 : {
710 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
711 : "Denomination key not specified at %s\n",
712 : label);
713 0 : GNUNET_assert (0);
714 : }
715 0 : ws = GNUNET_new (struct WithdrawState);
716 0 : ws->reserve_reference = reserve_reference;
717 0 : ws->pk = TALER_EXCHANGE_copy_denomination_key (dk);
718 0 : ws->expected_response_code = expected_response_code;
719 : {
720 0 : struct TALER_TESTING_Command cmd = {
721 : .cls = ws,
722 : .label = label,
723 : .run = &withdraw_run,
724 : .cleanup = &withdraw_cleanup,
725 : .traits = &withdraw_traits
726 : };
727 :
728 0 : return cmd;
729 : }
730 : }
731 :
732 :
733 : struct TALER_TESTING_Command
734 0 : TALER_TESTING_cmd_withdraw_with_retry (struct TALER_TESTING_Command cmd)
735 : {
736 : struct WithdrawState *ws;
737 :
738 0 : GNUNET_assert (&withdraw_run == cmd.run);
739 0 : ws = cmd.cls;
740 0 : ws->do_retry = NUM_RETRIES;
741 0 : return cmd;
742 : }
743 :
744 :
745 : /* end of testing_api_cmd_withdraw.c */
|