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