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