Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2018-2022 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_refresh.c
21 : * @brief commands for testing all "refresh" features.
22 : * @author Marcello Stanisci
23 : */
24 : #include "platform.h"
25 : #include "taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler_testing_lib.h"
28 : #include "taler_signatures.h"
29 : #include "backoff.h"
30 :
31 : /**
32 : * How long do we wait AT MOST when retrying?
33 : */
34 : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
35 : GNUNET_TIME_UNIT_MILLISECONDS, 100)
36 :
37 : /**
38 : * How often do we retry before giving up?
39 : */
40 : #define NUM_RETRIES 5
41 :
42 : /**
43 : * How long do we wait AT MOST when retrying?
44 : */
45 : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
46 : GNUNET_TIME_UNIT_MILLISECONDS, 100)
47 :
48 : /**
49 : * Information about a fresh coin generated by the refresh
50 : * operation.
51 : */
52 : struct TALER_TESTING_FreshCoinData
53 : {
54 :
55 : /**
56 : * If @e amount is NULL, this specifies the denomination key to
57 : * use. Otherwise, this will be set (by the interpreter) to the
58 : * denomination PK matching @e amount.
59 : */
60 : const struct TALER_EXCHANGE_DenomPublicKey *pk;
61 :
62 : /**
63 : * Set (by the interpreter) to the exchange's signature over the
64 : * coin's public key.
65 : */
66 : struct TALER_DenominationSignature sig;
67 :
68 : /**
69 : * Set (by the interpreter) to the coin's private key.
70 : */
71 : struct TALER_CoinSpendPrivateKeyP coin_priv;
72 :
73 : /*
74 : * Fresh age commitment for the coin with proof and its hash, NULL if not
75 : * applicable.
76 : */
77 : struct TALER_AgeCommitmentProof *age_commitment_proof;
78 : struct TALER_AgeCommitmentHash *h_age_commitment;
79 :
80 : /**
81 : * The blinding key (needed for recoup operations).
82 : */
83 : union TALER_DenominationBlindingKeyP blinding_key;
84 :
85 : };
86 :
87 :
88 : /**
89 : * State for a "refresh melt" command.
90 : */
91 : struct RefreshMeltState
92 : {
93 :
94 : /**
95 : * Reference to reserve_withdraw operations for coin to
96 : * be used for the /refresh/melt operation.
97 : */
98 : const char *coin_reference;
99 :
100 : /**
101 : * Data used in the refresh operation.
102 : */
103 : struct TALER_EXCHANGE_RefreshData refresh_data;
104 :
105 : /**
106 : * Reference to a previous melt command.
107 : */
108 : const char *melt_reference;
109 :
110 : /**
111 : * Melt handle while operation is running.
112 : */
113 : struct TALER_EXCHANGE_MeltHandle *rmh;
114 :
115 : /**
116 : * Interpreter state.
117 : */
118 : struct TALER_TESTING_Interpreter *is;
119 :
120 : /**
121 : * Array of the denomination public keys
122 : * corresponding to the @e num_fresh_coins;
123 : */
124 : struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
125 :
126 : /**
127 : * Array of @e num_fresh_coins of results from
128 : * the melt operation.
129 : */
130 : struct TALER_EXCHANGE_MeltBlindingDetail *mbds;
131 :
132 : /**
133 : * Entropy seed for the refresh-melt operation.
134 : */
135 : struct TALER_RefreshMasterSecretP rms;
136 :
137 : /**
138 : * Private key of the dirty coin being melted.
139 : */
140 : const struct TALER_CoinSpendPrivateKeyP *melt_priv;
141 :
142 : /**
143 : * Task scheduled to try later.
144 : */
145 : struct GNUNET_SCHEDULER_Task *retry_task;
146 :
147 : /**
148 : * How long do we wait until we retry?
149 : */
150 : struct GNUNET_TIME_Relative backoff;
151 :
152 : /**
153 : * How long did we wait in total for retries?
154 : */
155 : struct GNUNET_TIME_Relative total_backoff;
156 :
157 : /**
158 : * Amounts to be generated during melt.
159 : */
160 : const char **melt_fresh_amounts;
161 :
162 : /**
163 : * Number of fresh coins generated by the melt.
164 : */
165 : unsigned int num_fresh_coins;
166 :
167 : /**
168 : * Expected HTTP response code.
169 : */
170 : unsigned int expected_response_code;
171 :
172 : /**
173 : * if set to #GNUNET_YES, then two /refresh/melt operations
174 : * will be performed. This is needed to trigger the logic
175 : * that manages those already-made requests. Note: it
176 : * is not possible to just copy-and-paste a test refresh melt
177 : * CMD to have the same effect, because every data preparation
178 : * generates new planchets that (in turn) make the whole "hash"
179 : * different from any previous one, therefore NOT allowing the
180 : * exchange to pick any previous /rerfesh/melt operation from
181 : * the database.
182 : */
183 : bool double_melt;
184 :
185 : /**
186 : * How often should we retry on (transient) failures?
187 : */
188 : unsigned int do_retry;
189 :
190 : /**
191 : * Set by the melt callback as it comes from the exchange.
192 : */
193 : uint16_t noreveal_index;
194 : };
195 :
196 :
197 : /**
198 : * State for a "refresh reveal" CMD.
199 : */
200 : struct RefreshRevealState
201 : {
202 : /**
203 : * Link to a "refresh melt" command.
204 : */
205 : const char *melt_reference;
206 :
207 : /**
208 : * Reveal handle while operation is running.
209 : */
210 : struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
211 :
212 : /**
213 : * Convenience struct to keep in one place all the
214 : * data related to one fresh coin, set by the reveal callback
215 : * as it comes from the exchange.
216 : */
217 : struct TALER_TESTING_FreshCoinData *fresh_coins;
218 :
219 : /**
220 : * Array of @e num_fresh_coins planchet secrets derived
221 : * from the transfer secret per fresh coin.
222 : */
223 : struct TALER_PlanchetMasterSecretP *psa;
224 :
225 : /**
226 : * Interpreter state.
227 : */
228 : struct TALER_TESTING_Interpreter *is;
229 :
230 : /**
231 : * Task scheduled to try later.
232 : */
233 : struct GNUNET_SCHEDULER_Task *retry_task;
234 :
235 : /**
236 : * How long do we wait until we retry?
237 : */
238 : struct GNUNET_TIME_Relative backoff;
239 :
240 : /**
241 : * How long did we wait in total for retries?
242 : */
243 : struct GNUNET_TIME_Relative total_backoff;
244 :
245 : /**
246 : * Number of fresh coins withdrawn, set by the
247 : * reveal callback as it comes from the exchange,
248 : * it is the length of the @e fresh_coins array.
249 : */
250 : unsigned int num_fresh_coins;
251 :
252 : /**
253 : * Expected HTTP response code.
254 : */
255 : unsigned int expected_response_code;
256 :
257 : /**
258 : * How often should we retry on (transient) failures?
259 : */
260 : unsigned int do_retry;
261 :
262 : };
263 :
264 :
265 : /**
266 : * State for a "refresh link" CMD.
267 : */
268 : struct RefreshLinkState
269 : {
270 : /**
271 : * Link to a "refresh reveal" command.
272 : */
273 : const char *reveal_reference;
274 :
275 : /**
276 : * Handle to the ongoing operation.
277 : */
278 : struct TALER_EXCHANGE_LinkHandle *rlh;
279 :
280 : /**
281 : * Interpreter state.
282 : */
283 : struct TALER_TESTING_Interpreter *is;
284 :
285 : /**
286 : * Task scheduled to try later.
287 : */
288 : struct GNUNET_SCHEDULER_Task *retry_task;
289 :
290 : /**
291 : * How long do we wait until we retry?
292 : */
293 : struct GNUNET_TIME_Relative backoff;
294 :
295 : /**
296 : * How long did we wait in total for retries?
297 : */
298 : struct GNUNET_TIME_Relative total_backoff;
299 :
300 : /**
301 : * Expected HTTP response code.
302 : */
303 : unsigned int expected_response_code;
304 :
305 : /**
306 : * How often should we retry on (transient) failures?
307 : */
308 : unsigned int do_retry;
309 :
310 : };
311 :
312 :
313 : /**
314 : * Run the command.
315 : *
316 : * @param cls closure.
317 : * @param cmd the command to execute.
318 : * @param is the interpreter state.
319 : */
320 : static void
321 : refresh_reveal_run (void *cls,
322 : const struct TALER_TESTING_Command *cmd,
323 : struct TALER_TESTING_Interpreter *is);
324 :
325 :
326 : /**
327 : * Task scheduled to re-try #refresh_reveal_run.
328 : *
329 : * @param cls a `struct RefreshRevealState`
330 : */
331 : static void
332 0 : do_reveal_retry (void *cls)
333 : {
334 0 : struct RefreshRevealState *rrs = cls;
335 :
336 0 : rrs->retry_task = NULL;
337 0 : rrs->is->commands[rrs->is->ip].last_req_time
338 0 : = GNUNET_TIME_absolute_get ();
339 0 : refresh_reveal_run (rrs,
340 : NULL,
341 : rrs->is);
342 0 : }
343 :
344 :
345 : /**
346 : * "refresh reveal" request callback; it checks that the response
347 : * code is expected and copies into its command's state the data
348 : * coming from the exchange, namely the fresh coins.
349 : *
350 : * @param cls closure, a `struct RefreshRevealState`
351 : * @param rr HTTP response details
352 : */
353 : static void
354 0 : reveal_cb (void *cls,
355 : const struct TALER_EXCHANGE_RevealResult *rr)
356 : {
357 0 : struct RefreshRevealState *rrs = cls;
358 0 : const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
359 : const struct TALER_TESTING_Command *melt_cmd;
360 :
361 0 : rrs->rrh = NULL;
362 0 : if (rrs->expected_response_code != hr->http_status)
363 : {
364 0 : if (0 != rrs->do_retry)
365 : {
366 0 : rrs->do_retry--;
367 0 : if ( (0 == hr->http_status) ||
368 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
369 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
370 : {
371 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372 : "Retrying refresh reveal failed with %u/%d\n",
373 : hr->http_status,
374 : (int) hr->ec);
375 : /* on DB conflicts, do not use backoff */
376 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
377 0 : rrs->backoff = GNUNET_TIME_UNIT_ZERO;
378 : else
379 0 : rrs->backoff = GNUNET_TIME_randomized_backoff (rrs->backoff,
380 : MAX_BACKOFF);
381 0 : rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff,
382 : rrs->backoff);
383 0 : rrs->is->commands[rrs->is->ip].num_tries++;
384 0 : rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff,
385 : &do_reveal_retry,
386 : rrs);
387 0 : return;
388 : }
389 : }
390 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
391 : "Unexpected response code %u/%d to command %s in %s:%u\n",
392 : hr->http_status,
393 : (int) hr->ec,
394 : rrs->is->commands[rrs->is->ip].label,
395 : __FILE__,
396 : __LINE__);
397 0 : json_dumpf (hr->reply,
398 : stderr,
399 : 0);
400 0 : TALER_TESTING_interpreter_fail (rrs->is);
401 0 : return;
402 : }
403 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is,
404 : rrs->melt_reference);
405 0 : if (NULL == melt_cmd)
406 : {
407 0 : GNUNET_break (0);
408 0 : TALER_TESTING_interpreter_fail (rrs->is);
409 0 : return;
410 : }
411 0 : switch (hr->http_status)
412 : {
413 0 : case MHD_HTTP_OK:
414 0 : rrs->num_fresh_coins = rr->details.success.num_coins;
415 0 : rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
416 : struct TALER_PlanchetMasterSecretP);
417 0 : rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
418 : struct TALER_TESTING_FreshCoinData);
419 0 : for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
420 : {
421 0 : const struct TALER_EXCHANGE_RevealedCoinInfo *coin
422 0 : = &rr->details.success.coins[i];
423 0 : struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
424 :
425 0 : rrs->psa[i] = coin->ps;
426 0 : fc->blinding_key = coin->bks;
427 0 : if (GNUNET_OK !=
428 0 : TALER_TESTING_get_trait_denom_pub (melt_cmd,
429 : i,
430 : &fc->pk))
431 : {
432 0 : GNUNET_break (0);
433 0 : TALER_TESTING_interpreter_fail (rrs->is);
434 0 : return;
435 : }
436 0 : fc->coin_priv = coin->coin_priv;
437 0 : fc->age_commitment_proof = coin->age_commitment_proof;
438 0 : fc->h_age_commitment = coin->h_age_commitment;
439 :
440 0 : TALER_denom_sig_deep_copy (&fc->sig,
441 : &coin->sig);
442 : }
443 0 : if (0 != rrs->total_backoff.rel_value_us)
444 : {
445 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
446 : "Total reveal backoff for %s was %s\n",
447 : rrs->is->commands[rrs->is->ip].label,
448 : GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff,
449 : GNUNET_YES));
450 : }
451 0 : break;
452 0 : default:
453 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
454 : "Unknown HTTP status %u/%d\n",
455 : hr->http_status,
456 : (int) hr->ec);
457 : }
458 0 : TALER_TESTING_interpreter_next (rrs->is);
459 : }
460 :
461 :
462 : /**
463 : * Run the command.
464 : *
465 : * @param cls closure.
466 : * @param cmd the command to execute.
467 : * @param is the interpreter state.
468 : */
469 : static void
470 : melt_run (void *cls,
471 : const struct TALER_TESTING_Command *cmd,
472 : struct TALER_TESTING_Interpreter *is);
473 :
474 :
475 : /**
476 : * Run the command.
477 : *
478 : * @param cls closure.
479 : * @param cmd the command to execute.
480 : * @param is the interpreter state.
481 : */
482 : static void
483 0 : refresh_reveal_run (void *cls,
484 : const struct TALER_TESTING_Command *cmd,
485 : struct TALER_TESTING_Interpreter *is)
486 : {
487 0 : struct RefreshRevealState *rrs = cls;
488 : struct RefreshMeltState *rms;
489 : const struct TALER_TESTING_Command *melt_cmd;
490 :
491 0 : rrs->is = is;
492 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
493 : rrs->melt_reference);
494 0 : if (NULL == melt_cmd)
495 : {
496 0 : GNUNET_break (0);
497 0 : TALER_TESTING_interpreter_fail (rrs->is);
498 0 : return;
499 : }
500 0 : GNUNET_assert (melt_cmd->run == &melt_run);
501 0 : rms = melt_cmd->cls;
502 0 : {
503 0 : struct TALER_ExchangeWithdrawValues alg_values[rms->num_fresh_coins];
504 :
505 0 : for (unsigned int i = 0; i<rms->num_fresh_coins; i++)
506 0 : alg_values[i] = rms->mbds[i].alg_value;
507 0 : rrs->rrh = TALER_EXCHANGE_refreshes_reveal (is->exchange,
508 0 : &rms->rms,
509 0 : &rms->refresh_data,
510 : rms->num_fresh_coins,
511 : alg_values,
512 0 : rms->noreveal_index,
513 : &reveal_cb,
514 : rrs);
515 : }
516 0 : if (NULL == rrs->rrh)
517 : {
518 0 : GNUNET_break (0);
519 0 : TALER_TESTING_interpreter_fail (is);
520 0 : return;
521 : }
522 : }
523 :
524 :
525 : /**
526 : * Free the state from a "refresh reveal" CMD, and possibly
527 : * cancel a pending operation thereof.
528 : *
529 : * @param cls closure.
530 : * @param cmd the command which is being cleaned up.
531 : */
532 : static void
533 0 : refresh_reveal_cleanup (void *cls,
534 : const struct TALER_TESTING_Command *cmd)
535 : {
536 0 : struct RefreshRevealState *rrs = cls;
537 :
538 : (void) cmd;
539 0 : if (NULL != rrs->rrh)
540 : {
541 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
542 : "Command %u (%s) did not complete\n",
543 : rrs->is->ip,
544 : cmd->label);
545 0 : TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
546 0 : rrs->rrh = NULL;
547 : }
548 0 : if (NULL != rrs->retry_task)
549 : {
550 0 : GNUNET_SCHEDULER_cancel (rrs->retry_task);
551 0 : rrs->retry_task = NULL;
552 : }
553 :
554 0 : for (unsigned int j = 0; j < rrs->num_fresh_coins; j++)
555 0 : TALER_denom_sig_free (&rrs->fresh_coins[j].sig);
556 :
557 0 : GNUNET_free (rrs->fresh_coins);
558 0 : GNUNET_free (rrs->psa);
559 0 : rrs->num_fresh_coins = 0;
560 0 : GNUNET_free (rrs);
561 0 : }
562 :
563 :
564 : /**
565 : * Run the command.
566 : *
567 : * @param cls closure.
568 : * @param cmd the command to execute.
569 : * @param is the interpreter state.
570 : */
571 : static void
572 : refresh_link_run (void *cls,
573 : const struct TALER_TESTING_Command *cmd,
574 : struct TALER_TESTING_Interpreter *is);
575 :
576 :
577 : /**
578 : * Task scheduled to re-try #refresh_link_run.
579 : *
580 : * @param cls a `struct RefreshLinkState`
581 : */
582 : static void
583 0 : do_link_retry (void *cls)
584 : {
585 0 : struct RefreshLinkState *rls = cls;
586 :
587 0 : rls->retry_task = NULL;
588 0 : rls->is->commands[rls->is->ip].last_req_time
589 0 : = GNUNET_TIME_absolute_get ();
590 0 : refresh_link_run (rls,
591 : NULL,
592 : rls->is);
593 0 : }
594 :
595 :
596 : /**
597 : * "refresh link" operation callback, checks that HTTP response
598 : * code is expected _and_ that all the linked coins were actually
599 : * withdrawn by the "refresh reveal" CMD.
600 : *
601 : * @param cls closure.
602 : * @param lr HTTP response details
603 : */
604 : static void
605 0 : link_cb (void *cls,
606 : const struct TALER_EXCHANGE_LinkResult *lr)
607 : {
608 0 : struct RefreshLinkState *rls = cls;
609 0 : const struct TALER_EXCHANGE_HttpResponse *hr = &lr->hr;
610 : const struct TALER_TESTING_Command *reveal_cmd;
611 0 : struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip];
612 : unsigned int found;
613 : const unsigned int *num_fresh_coins;
614 :
615 0 : rls->rlh = NULL;
616 0 : if (rls->expected_response_code != hr->http_status)
617 : {
618 0 : if (0 != rls->do_retry)
619 : {
620 0 : rls->do_retry--;
621 0 : if ( (0 == hr->http_status) ||
622 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
623 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
624 : {
625 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
626 : "Retrying refresh link failed with %u/%d\n",
627 : hr->http_status,
628 : (int) hr->ec);
629 : /* on DB conflicts, do not use backoff */
630 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
631 0 : rls->backoff = GNUNET_TIME_UNIT_ZERO;
632 : else
633 0 : rls->backoff = GNUNET_TIME_randomized_backoff (rls->backoff,
634 : MAX_BACKOFF);
635 0 : rls->total_backoff = GNUNET_TIME_relative_add (rls->total_backoff,
636 : rls->backoff);
637 0 : rls->is->commands[rls->is->ip].num_tries++;
638 0 : rls->retry_task = GNUNET_SCHEDULER_add_delayed (rls->backoff,
639 : &do_link_retry,
640 : rls);
641 0 : return;
642 : }
643 : }
644 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 : "Unexpected response code %u/%d to command %s in %s:%u\n",
646 : hr->http_status,
647 : (int) hr->ec,
648 : link_cmd->label,
649 : __FILE__,
650 : __LINE__);
651 0 : json_dumpf (hr->reply,
652 : stderr,
653 : 0);
654 0 : TALER_TESTING_interpreter_fail (rls->is);
655 0 : return;
656 : }
657 0 : reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
658 : rls->reveal_reference);
659 0 : if (NULL == reveal_cmd)
660 : {
661 0 : GNUNET_break (0);
662 0 : TALER_TESTING_interpreter_fail (rls->is);
663 0 : return;
664 : }
665 :
666 0 : switch (hr->http_status)
667 : {
668 0 : case MHD_HTTP_OK:
669 : /* check that number of coins returned matches */
670 0 : if (GNUNET_OK !=
671 0 : TALER_TESTING_get_trait_array_length (reveal_cmd,
672 : &num_fresh_coins))
673 : {
674 0 : GNUNET_break (0);
675 0 : TALER_TESTING_interpreter_fail (rls->is);
676 0 : return;
677 : }
678 0 : if (lr->details.success.num_coins != *num_fresh_coins)
679 : {
680 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681 : "Unexpected number of fresh coins: %d vs %d in %s:%u\n",
682 : lr->details.success.num_coins,
683 : *num_fresh_coins,
684 : __FILE__,
685 : __LINE__);
686 0 : TALER_TESTING_interpreter_fail (rls->is);
687 0 : return;
688 : }
689 : /* check that the coins match */
690 0 : for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
691 0 : for (unsigned int j = i + 1; j<lr->details.success.num_coins; j++)
692 0 : if (0 ==
693 0 : GNUNET_memcmp (&lr->details.success.coins[i].coin_priv,
694 : &lr->details.success.coins[j].coin_priv))
695 0 : GNUNET_break (0);
696 : /* Note: coins might be legitimately permutated in here... */
697 0 : found = 0;
698 :
699 : /* Will point to the pointer inside the cmd state. */
700 : {
701 0 : const struct TALER_TESTING_FreshCoinData **fc = NULL;
702 :
703 0 : if (GNUNET_OK !=
704 0 : TALER_TESTING_get_trait_fresh_coins (reveal_cmd,
705 : &fc))
706 : {
707 0 : GNUNET_break (0);
708 0 : TALER_TESTING_interpreter_fail (rls->is);
709 0 : return;
710 : }
711 :
712 0 : for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
713 : {
714 0 : const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i
715 0 : = &lr->details.success.coins[i];
716 :
717 0 : for (unsigned int j = 0; j<lr->details.success.num_coins; j++)
718 : {
719 0 : const struct TALER_TESTING_FreshCoinData *fcj
720 0 : = &(*fc)[j];
721 :
722 0 : if ( (0 ==
723 0 : GNUNET_memcmp (&fcj->coin_priv,
724 0 : &lci_i->coin_priv)) &&
725 : (0 ==
726 0 : TALER_denom_sig_cmp (&fcj->sig,
727 0 : &lci_i->sig)) &&
728 : (0 ==
729 0 : TALER_denom_pub_cmp (&fcj->pk->key,
730 : &lci_i->pub)) )
731 : {
732 0 : found++;
733 0 : break;
734 : }
735 : } /* for j*/
736 : } /* for i */
737 : }
738 0 : if (found != lr->details.success.num_coins)
739 : {
740 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
741 : "Only %u/%u coins match expectations\n",
742 : found,
743 : lr->details.success.num_coins);
744 0 : GNUNET_break (0);
745 0 : TALER_TESTING_interpreter_fail (rls->is);
746 0 : return;
747 : }
748 0 : if (0 != rls->total_backoff.rel_value_us)
749 : {
750 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
751 : "Total link backoff for %s was %s\n",
752 : rls->is->commands[rls->is->ip].label,
753 : GNUNET_STRINGS_relative_time_to_string (rls->total_backoff,
754 : GNUNET_YES));
755 : }
756 0 : break;
757 0 : default:
758 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
759 : "Unknown HTTP response code %u/%d.\n",
760 : hr->http_status,
761 : hr->ec);
762 : }
763 0 : TALER_TESTING_interpreter_next (rls->is);
764 : }
765 :
766 :
767 : /**
768 : * Run the command.
769 : *
770 : * @param cls closure.
771 : * @param cmd the command to execute.
772 : * @param is the interpreter state.
773 : */
774 : static void
775 0 : refresh_link_run (void *cls,
776 : const struct TALER_TESTING_Command *cmd,
777 : struct TALER_TESTING_Interpreter *is)
778 : {
779 0 : struct RefreshLinkState *rls = cls;
780 : struct RefreshRevealState *rrs;
781 : struct RefreshMeltState *rms;
782 : const struct TALER_TESTING_Command *reveal_cmd;
783 : const struct TALER_TESTING_Command *melt_cmd;
784 : const struct TALER_TESTING_Command *coin_cmd;
785 :
786 : (void) cmd;
787 0 : rls->is = is;
788 0 : reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
789 : rls->reveal_reference);
790 0 : if (NULL == reveal_cmd)
791 : {
792 0 : GNUNET_break (0);
793 0 : TALER_TESTING_interpreter_fail (rls->is);
794 0 : return;
795 : }
796 0 : rrs = reveal_cmd->cls;
797 0 : melt_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
798 : rrs->melt_reference);
799 0 : if (NULL == melt_cmd)
800 : {
801 0 : GNUNET_break (0);
802 0 : TALER_TESTING_interpreter_fail (rls->is);
803 0 : return;
804 : }
805 :
806 : /* find reserve_withdraw command */
807 0 : GNUNET_assert (melt_cmd->run == &melt_run);
808 0 : rms = melt_cmd->cls;
809 0 : coin_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
810 : rms->coin_reference);
811 0 : if (NULL == coin_cmd)
812 : {
813 0 : GNUNET_break (0);
814 0 : TALER_TESTING_interpreter_fail (rls->is);
815 0 : return;
816 : }
817 :
818 : const struct TALER_CoinSpendPrivateKeyP *coin_priv;
819 0 : if (GNUNET_OK !=
820 0 : TALER_TESTING_get_trait_coin_priv (coin_cmd,
821 : 0,
822 : &coin_priv))
823 : {
824 0 : GNUNET_break (0);
825 0 : TALER_TESTING_interpreter_fail (rls->is);
826 0 : return;
827 : }
828 :
829 : /* finally, use private key from withdraw sign command */
830 0 : rls->rlh = TALER_EXCHANGE_link (is->exchange,
831 : coin_priv,
832 : rms->refresh_data.melt_age_commitment_proof,
833 : &link_cb,
834 : rls);
835 :
836 0 : if (NULL == rls->rlh)
837 : {
838 0 : GNUNET_break (0);
839 0 : TALER_TESTING_interpreter_fail (rls->is);
840 0 : return;
841 : }
842 : }
843 :
844 :
845 : /**
846 : * Free the state of the "refresh link" CMD, and possibly
847 : * cancel a operation thereof.
848 : *
849 : * @param cls closure
850 : * @param cmd the command which is being cleaned up.
851 : */
852 : static void
853 0 : refresh_link_cleanup (void *cls,
854 : const struct TALER_TESTING_Command *cmd)
855 : {
856 0 : struct RefreshLinkState *rls = cls;
857 :
858 0 : if (NULL != rls->rlh)
859 : {
860 :
861 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
862 : "Command %u (%s) did not complete\n",
863 : rls->is->ip,
864 : cmd->label);
865 0 : TALER_EXCHANGE_link_cancel (rls->rlh);
866 0 : rls->rlh = NULL;
867 : }
868 0 : if (NULL != rls->retry_task)
869 : {
870 0 : GNUNET_SCHEDULER_cancel (rls->retry_task);
871 0 : rls->retry_task = NULL;
872 : }
873 0 : GNUNET_free (rls);
874 0 : }
875 :
876 :
877 : /**
878 : * Task scheduled to re-try #melt_run.
879 : *
880 : * @param cls a `struct RefreshMeltState`
881 : */
882 : static void
883 0 : do_melt_retry (void *cls)
884 : {
885 0 : struct RefreshMeltState *rms = cls;
886 :
887 0 : rms->retry_task = NULL;
888 0 : rms->is->commands[rms->is->ip].last_req_time
889 0 : = GNUNET_TIME_absolute_get ();
890 0 : melt_run (rms,
891 : NULL,
892 : rms->is);
893 0 : }
894 :
895 :
896 : /**
897 : * Callback for a "refresh melt" operation; checks if the HTTP
898 : * response code is okay and re-run the melt operation if the
899 : * CMD was set to do so.
900 : *
901 : * @param cls closure.
902 : * @param mr melt response details
903 : */
904 : static void
905 0 : melt_cb (void *cls,
906 : const struct TALER_EXCHANGE_MeltResponse *mr)
907 : {
908 0 : struct RefreshMeltState *rms = cls;
909 0 : const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
910 :
911 0 : rms->rmh = NULL;
912 0 : if (rms->expected_response_code != hr->http_status)
913 : {
914 0 : if (0 != rms->do_retry)
915 : {
916 0 : rms->do_retry--;
917 0 : if ( (0 == hr->http_status) ||
918 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
919 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
920 : {
921 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
922 : "Retrying refresh melt failed with %u/%d\n",
923 : hr->http_status,
924 : (int) hr->ec);
925 : /* on DB conflicts, do not use backoff */
926 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
927 0 : rms->backoff = GNUNET_TIME_UNIT_ZERO;
928 : else
929 0 : rms->backoff = GNUNET_TIME_randomized_backoff (rms->backoff,
930 : MAX_BACKOFF);
931 0 : rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff,
932 : rms->backoff);
933 0 : rms->is->commands[rms->is->ip].num_tries++;
934 0 : rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff,
935 : &do_melt_retry,
936 : rms);
937 0 : return;
938 : }
939 : }
940 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
941 : "Unexpected response code %u/%d to command %s in %s:%u\n",
942 : hr->http_status,
943 : (int) hr->ec,
944 : rms->is->commands[rms->is->ip].label,
945 : __FILE__,
946 : __LINE__);
947 0 : json_dumpf (hr->reply,
948 : stderr,
949 : 0);
950 0 : TALER_TESTING_interpreter_fail (rms->is);
951 0 : return;
952 : }
953 0 : if (MHD_HTTP_OK == hr->http_status)
954 : {
955 0 : rms->noreveal_index = mr->details.success.noreveal_index;
956 0 : if (mr->details.success.num_mbds != rms->num_fresh_coins)
957 : {
958 0 : GNUNET_break (0);
959 0 : TALER_TESTING_interpreter_fail (rms->is);
960 0 : return;
961 : }
962 0 : GNUNET_free (rms->mbds);
963 0 : rms->mbds = GNUNET_memdup (mr->details.success.mbds,
964 : mr->details.success.num_mbds
965 : * sizeof (struct
966 : TALER_EXCHANGE_MeltBlindingDetail));
967 : }
968 0 : if (0 != rms->total_backoff.rel_value_us)
969 : {
970 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
971 : "Total melt backoff for %s was %s\n",
972 : rms->is->commands[rms->is->ip].label,
973 : GNUNET_STRINGS_relative_time_to_string (rms->total_backoff,
974 : GNUNET_YES));
975 : }
976 0 : if (rms->double_melt)
977 : {
978 0 : TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
979 : rms->is->commands[rms->is->ip].label);
980 0 : rms->rmh = TALER_EXCHANGE_melt (rms->is->exchange,
981 0 : &rms->rms,
982 0 : &rms->refresh_data,
983 : &melt_cb,
984 : rms);
985 0 : rms->double_melt = false;
986 0 : return;
987 : }
988 0 : TALER_TESTING_interpreter_next (rms->is);
989 : }
990 :
991 :
992 : /**
993 : * Run the command.
994 : *
995 : * @param cls closure.
996 : * @param cmd the command to execute.
997 : * @param is the interpreter state.
998 : */
999 : static void
1000 0 : melt_run (void *cls,
1001 : const struct TALER_TESTING_Command *cmd,
1002 : struct TALER_TESTING_Interpreter *is)
1003 : {
1004 0 : struct RefreshMeltState *rms = cls;
1005 : unsigned int num_fresh_coins;
1006 0 : const char *default_melt_fresh_amounts[] = {
1007 : "EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
1008 : NULL
1009 : };
1010 : const char **melt_fresh_amounts;
1011 :
1012 : (void) cmd;
1013 0 : if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
1014 0 : melt_fresh_amounts = default_melt_fresh_amounts;
1015 0 : rms->is = is;
1016 0 : rms->noreveal_index = UINT16_MAX;
1017 0 : TALER_refresh_master_setup_random (&rms->rms);
1018 0 : for (num_fresh_coins = 0;
1019 0 : NULL != melt_fresh_amounts[num_fresh_coins];
1020 0 : num_fresh_coins++)
1021 : ;
1022 0 : rms->num_fresh_coins = num_fresh_coins;
1023 0 : rms->fresh_pks = GNUNET_new_array (
1024 : num_fresh_coins,
1025 : struct TALER_EXCHANGE_DenomPublicKey);
1026 : {
1027 : struct TALER_Amount melt_amount;
1028 : struct TALER_Amount fresh_amount;
1029 : const struct TALER_AgeCommitmentProof *age_commitment_proof;
1030 : const struct TALER_AgeCommitmentHash *h_age_commitment;
1031 : const struct TALER_DenominationSignature *melt_sig;
1032 : const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
1033 : const struct TALER_TESTING_Command *coin_command;
1034 : bool age_restricted;
1035 :
1036 0 : if (NULL == (coin_command
1037 0 : = TALER_TESTING_interpreter_lookup_command (
1038 : is,
1039 : rms->coin_reference)))
1040 : {
1041 0 : GNUNET_break (0);
1042 0 : TALER_TESTING_interpreter_fail (rms->is);
1043 0 : return;
1044 : }
1045 :
1046 0 : if (GNUNET_OK !=
1047 0 : TALER_TESTING_get_trait_coin_priv (coin_command,
1048 : 0,
1049 : &rms->melt_priv))
1050 : {
1051 0 : GNUNET_break (0);
1052 0 : TALER_TESTING_interpreter_fail (rms->is);
1053 0 : return;
1054 : }
1055 :
1056 0 : if (GNUNET_OK !=
1057 0 : TALER_TESTING_get_trait_age_commitment_proof (coin_command,
1058 : 0,
1059 : &age_commitment_proof))
1060 : {
1061 0 : GNUNET_break (0);
1062 0 : TALER_TESTING_interpreter_fail (rms->is);
1063 0 : return;
1064 : }
1065 :
1066 0 : if (GNUNET_OK !=
1067 0 : TALER_TESTING_get_trait_h_age_commitment (coin_command,
1068 : 0,
1069 : &h_age_commitment))
1070 : {
1071 0 : GNUNET_break (0);
1072 0 : TALER_TESTING_interpreter_fail (rms->is);
1073 0 : return;
1074 : }
1075 :
1076 0 : if (GNUNET_OK !=
1077 0 : TALER_TESTING_get_trait_denom_sig (coin_command,
1078 : 0,
1079 : &melt_sig))
1080 : {
1081 0 : GNUNET_break (0);
1082 0 : TALER_TESTING_interpreter_fail (rms->is);
1083 0 : return;
1084 : }
1085 :
1086 0 : if (GNUNET_OK !=
1087 0 : TALER_TESTING_get_trait_denom_pub (coin_command,
1088 : 0,
1089 : &melt_denom_pub))
1090 : {
1091 0 : GNUNET_break (0);
1092 0 : TALER_TESTING_interpreter_fail (rms->is);
1093 0 : return;
1094 : }
1095 :
1096 : /* Melt amount starts with the melt fee of the old coin; we'll add the
1097 : values and withdraw fees of the fresh coins next */
1098 0 : melt_amount = melt_denom_pub->fees.refresh;
1099 0 : age_restricted = melt_denom_pub->key.age_mask.bits != 0;
1100 0 : for (unsigned int i = 0; i<num_fresh_coins; i++)
1101 : {
1102 : const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
1103 :
1104 0 : if (GNUNET_OK !=
1105 0 : TALER_string_to_amount (melt_fresh_amounts[i],
1106 : &fresh_amount))
1107 : {
1108 0 : GNUNET_break (0);
1109 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1110 : "Failed to parse amount `%s' at index %u\n",
1111 : melt_fresh_amounts[i],
1112 : i);
1113 0 : TALER_TESTING_interpreter_fail (rms->is);
1114 0 : return;
1115 : }
1116 0 : fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
1117 : &fresh_amount,
1118 : age_restricted);
1119 0 : if (NULL == fresh_pk)
1120 : {
1121 0 : GNUNET_break (0);
1122 : /* Subroutine logs specific error */
1123 0 : TALER_TESTING_interpreter_fail (rms->is);
1124 0 : return;
1125 : }
1126 0 : GNUNET_assert (0 <=
1127 : TALER_amount_add (&melt_amount,
1128 : &melt_amount,
1129 : &fresh_amount));
1130 0 : GNUNET_assert (0 <=
1131 : TALER_amount_add (&melt_amount,
1132 : &melt_amount,
1133 : &fresh_pk->fees.withdraw));
1134 0 : rms->fresh_pks[i] = *fresh_pk;
1135 : /* Make a deep copy of the RSA key */
1136 0 : TALER_denom_pub_deep_copy (&rms->fresh_pks[i].key,
1137 : &fresh_pk->key);
1138 : } /* end for */
1139 :
1140 0 : rms->refresh_data.melt_priv = *rms->melt_priv;
1141 0 : rms->refresh_data.melt_amount = melt_amount;
1142 0 : rms->refresh_data.melt_sig = *melt_sig;
1143 0 : rms->refresh_data.melt_pk = *melt_denom_pub;
1144 0 : rms->refresh_data.melt_age_commitment_proof = age_commitment_proof;
1145 0 : rms->refresh_data.melt_h_age_commitment = h_age_commitment;
1146 0 : rms->refresh_data.fresh_pks = rms->fresh_pks;
1147 0 : rms->refresh_data.fresh_pks_len = num_fresh_coins;
1148 :
1149 0 : GNUNET_assert (age_restricted ==
1150 : (NULL != age_commitment_proof));
1151 :
1152 0 : rms->rmh = TALER_EXCHANGE_melt (is->exchange,
1153 0 : &rms->rms,
1154 0 : &rms->refresh_data,
1155 : &melt_cb,
1156 : rms);
1157 :
1158 0 : if (NULL == rms->rmh)
1159 : {
1160 0 : GNUNET_break (0);
1161 0 : TALER_TESTING_interpreter_fail (rms->is);
1162 0 : return;
1163 : }
1164 : }
1165 : }
1166 :
1167 :
1168 : /**
1169 : * Free the "refresh melt" CMD state, and possibly cancel a
1170 : * pending operation thereof.
1171 : *
1172 : * @param cls closure, must be a `struct RefreshMeltState`.
1173 : * @param cmd the command which is being cleaned up.
1174 : */
1175 : static void
1176 0 : melt_cleanup (void *cls,
1177 : const struct TALER_TESTING_Command *cmd)
1178 : {
1179 0 : struct RefreshMeltState *rms = cls;
1180 :
1181 : (void) cmd;
1182 0 : if (NULL != rms->rmh)
1183 : {
1184 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1185 : "Command %u (%s) did not complete\n",
1186 : rms->is->ip,
1187 : rms->is->commands[rms->is->ip].label);
1188 0 : TALER_EXCHANGE_melt_cancel (rms->rmh);
1189 0 : rms->rmh = NULL;
1190 : }
1191 0 : if (NULL != rms->retry_task)
1192 : {
1193 0 : GNUNET_SCHEDULER_cancel (rms->retry_task);
1194 0 : rms->retry_task = NULL;
1195 : }
1196 0 : if (NULL != rms->fresh_pks)
1197 : {
1198 0 : for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
1199 0 : TALER_denom_pub_free (&rms->fresh_pks[i].key);
1200 0 : GNUNET_free (rms->fresh_pks);
1201 : }
1202 0 : GNUNET_free (rms->mbds);
1203 0 : GNUNET_free (rms->melt_fresh_amounts);
1204 0 : GNUNET_free (rms);
1205 0 : }
1206 :
1207 :
1208 : /**
1209 : * Offer internal data to the "refresh melt" CMD.
1210 : *
1211 : * @param cls closure.
1212 : * @param[out] ret result (could be anything).
1213 : * @param trait name of the trait.
1214 : * @param index index number of the object to offer.
1215 : * @return #GNUNET_OK on success.
1216 : */
1217 : static enum GNUNET_GenericReturnValue
1218 0 : melt_traits (void *cls,
1219 : const void **ret,
1220 : const char *trait,
1221 : unsigned int index)
1222 : {
1223 0 : struct RefreshMeltState *rms = cls;
1224 :
1225 0 : if (index >= rms->num_fresh_coins)
1226 : {
1227 0 : GNUNET_break (0);
1228 0 : return GNUNET_SYSERR;
1229 : }
1230 : {
1231 : struct TALER_TESTING_Trait traits[] = {
1232 0 : TALER_TESTING_make_trait_denom_pub (index,
1233 0 : &rms->fresh_pks[index]),
1234 0 : TALER_TESTING_make_trait_coin_priv (index,
1235 : rms->melt_priv),
1236 0 : TALER_TESTING_make_trait_age_commitment_proof (
1237 : index,
1238 : rms->refresh_data.melt_age_commitment_proof),
1239 0 : TALER_TESTING_make_trait_h_age_commitment (
1240 : index,
1241 : rms->refresh_data.melt_h_age_commitment),
1242 0 : TALER_TESTING_make_trait_exchange_wd_value (index,
1243 0 : &rms->mbds[index].alg_value),
1244 0 : TALER_TESTING_make_trait_refresh_secret (&rms->rms),
1245 0 : TALER_TESTING_trait_end ()
1246 : };
1247 :
1248 0 : return TALER_TESTING_get_trait (traits,
1249 : ret,
1250 : trait,
1251 : index);
1252 : }
1253 : }
1254 :
1255 :
1256 : /**
1257 : * Parse list of amounts for melt operation.
1258 : *
1259 : * @param[in,out] rms where to store the list
1260 : * @param ap NULL-termianted list of amounts to be melted (one per fresh coin)
1261 : * @return #GNUNET_OK on success
1262 : */
1263 : static enum GNUNET_GenericReturnValue
1264 0 : parse_amounts (struct RefreshMeltState *rms,
1265 : va_list ap)
1266 : {
1267 : unsigned int len;
1268 : unsigned int off;
1269 : const char *amount;
1270 :
1271 0 : len = 0;
1272 0 : off = 0;
1273 0 : while (NULL != (amount = va_arg (ap, const char *)))
1274 : {
1275 0 : if (len == off)
1276 : {
1277 : struct TALER_Amount a;
1278 :
1279 0 : GNUNET_array_grow (rms->melt_fresh_amounts,
1280 : len,
1281 : off + 16);
1282 0 : if (GNUNET_OK !=
1283 0 : TALER_string_to_amount (amount, &a))
1284 : {
1285 0 : GNUNET_break (0);
1286 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1287 : "Failed to parse amount `%s' at index %u\n",
1288 : amount, off);
1289 0 : GNUNET_free (rms->melt_fresh_amounts);
1290 0 : rms->melt_fresh_amounts = NULL;
1291 0 : return GNUNET_SYSERR;
1292 : }
1293 0 : rms->melt_fresh_amounts[off++] = amount;
1294 : }
1295 : }
1296 0 : if (0 == off)
1297 0 : return GNUNET_OK; /* no amounts given == use defaults! */
1298 : /* ensure NULL-termination */
1299 0 : GNUNET_array_grow (rms->melt_fresh_amounts,
1300 : len,
1301 : off + 1);
1302 0 : return GNUNET_OK;
1303 : }
1304 :
1305 :
1306 : struct TALER_TESTING_Command
1307 0 : TALER_TESTING_cmd_melt (const char *label,
1308 : const char *coin_reference,
1309 : unsigned int expected_response_code,
1310 : ...)
1311 : {
1312 : struct RefreshMeltState *rms;
1313 : va_list ap;
1314 :
1315 0 : rms = GNUNET_new (struct RefreshMeltState);
1316 0 : rms->coin_reference = coin_reference;
1317 0 : rms->expected_response_code = expected_response_code;
1318 0 : va_start (ap,
1319 : expected_response_code);
1320 0 : GNUNET_assert (GNUNET_OK ==
1321 : parse_amounts (rms, ap));
1322 0 : va_end (ap);
1323 : {
1324 0 : struct TALER_TESTING_Command cmd = {
1325 : .label = label,
1326 : .cls = rms,
1327 : .run = &melt_run,
1328 : .cleanup = &melt_cleanup,
1329 : .traits = &melt_traits
1330 : };
1331 :
1332 0 : return cmd;
1333 : }
1334 : }
1335 :
1336 :
1337 : struct TALER_TESTING_Command
1338 0 : TALER_TESTING_cmd_melt_double (const char *label,
1339 : const char *coin_reference,
1340 : unsigned int expected_response_code,
1341 : ...)
1342 : {
1343 : struct RefreshMeltState *rms;
1344 : va_list ap;
1345 :
1346 0 : rms = GNUNET_new (struct RefreshMeltState);
1347 0 : rms->coin_reference = coin_reference;
1348 0 : rms->expected_response_code = expected_response_code;
1349 0 : rms->double_melt = true;
1350 0 : va_start (ap,
1351 : expected_response_code);
1352 0 : GNUNET_assert (GNUNET_OK ==
1353 : parse_amounts (rms, ap));
1354 0 : va_end (ap);
1355 : {
1356 0 : struct TALER_TESTING_Command cmd = {
1357 : .label = label,
1358 : .cls = rms,
1359 : .run = &melt_run,
1360 : .cleanup = &melt_cleanup,
1361 : .traits = &melt_traits
1362 : };
1363 :
1364 0 : return cmd;
1365 : }
1366 : }
1367 :
1368 :
1369 : struct TALER_TESTING_Command
1370 0 : TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
1371 : {
1372 : struct RefreshMeltState *rms;
1373 :
1374 0 : GNUNET_assert (&melt_run == cmd.run);
1375 0 : rms = cmd.cls;
1376 0 : rms->do_retry = NUM_RETRIES;
1377 0 : return cmd;
1378 : }
1379 :
1380 :
1381 : /**
1382 : * Offer internal data from a "refresh reveal" CMD.
1383 : *
1384 : * @param cls closure.
1385 : * @param[out] ret result (could be anything).
1386 : * @param trait name of the trait.
1387 : * @param index index number of the object to offer.
1388 : * @return #GNUNET_OK on success.
1389 : */
1390 : static enum GNUNET_GenericReturnValue
1391 0 : refresh_reveal_traits (void *cls,
1392 : const void **ret,
1393 : const char *trait,
1394 : unsigned int index)
1395 : {
1396 0 : struct RefreshRevealState *rrs = cls;
1397 :
1398 0 : if (index >= rrs->num_fresh_coins)
1399 0 : return GNUNET_SYSERR;
1400 :
1401 : {
1402 : struct TALER_TESTING_Trait traits[] = {
1403 0 : TALER_TESTING_make_trait_coin_priv (
1404 : index,
1405 0 : &rrs->fresh_coins[index].coin_priv),
1406 0 : TALER_TESTING_make_trait_age_commitment_proof (
1407 : index,
1408 0 : rrs->fresh_coins[index].age_commitment_proof),
1409 0 : TALER_TESTING_make_trait_h_age_commitment (
1410 : index,
1411 0 : rrs->fresh_coins[index].h_age_commitment),
1412 0 : TALER_TESTING_make_trait_denom_pub (
1413 : index,
1414 0 : rrs->fresh_coins[index].pk),
1415 0 : TALER_TESTING_make_trait_denom_sig (
1416 : index,
1417 0 : &rrs->fresh_coins[index].sig),
1418 0 : TALER_TESTING_make_trait_blinding_key (
1419 : index,
1420 0 : &rrs->fresh_coins[index].blinding_key),
1421 0 : TALER_TESTING_make_trait_array_length (
1422 0 : &rrs->num_fresh_coins),
1423 0 : TALER_TESTING_make_trait_fresh_coins (
1424 0 : (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins),
1425 0 : TALER_TESTING_make_trait_planchet_secrets (index,
1426 0 : &rrs->psa[index]),
1427 0 : TALER_TESTING_trait_end ()
1428 : };
1429 0 : return TALER_TESTING_get_trait (traits,
1430 : ret,
1431 : trait,
1432 : index);
1433 : }
1434 : }
1435 :
1436 :
1437 : struct TALER_TESTING_Command
1438 0 : TALER_TESTING_cmd_refresh_reveal (const char *label,
1439 : const char *melt_reference,
1440 : unsigned int expected_response_code)
1441 : {
1442 : struct RefreshRevealState *rrs;
1443 :
1444 0 : rrs = GNUNET_new (struct RefreshRevealState);
1445 0 : rrs->melt_reference = melt_reference;
1446 0 : rrs->expected_response_code = expected_response_code;
1447 : {
1448 0 : struct TALER_TESTING_Command cmd = {
1449 : .cls = rrs,
1450 : .label = label,
1451 : .run = &refresh_reveal_run,
1452 : .cleanup = &refresh_reveal_cleanup,
1453 : .traits = &refresh_reveal_traits
1454 : };
1455 :
1456 0 : return cmd;
1457 : }
1458 : }
1459 :
1460 :
1461 : struct TALER_TESTING_Command
1462 0 : TALER_TESTING_cmd_refresh_reveal_with_retry (struct TALER_TESTING_Command cmd)
1463 : {
1464 : struct RefreshRevealState *rrs;
1465 :
1466 0 : GNUNET_assert (&refresh_reveal_run == cmd.run);
1467 0 : rrs = cmd.cls;
1468 0 : rrs->do_retry = NUM_RETRIES;
1469 0 : return cmd;
1470 : }
1471 :
1472 :
1473 : struct TALER_TESTING_Command
1474 0 : TALER_TESTING_cmd_refresh_link (const char *label,
1475 : const char *reveal_reference,
1476 : unsigned int expected_response_code)
1477 : {
1478 : struct RefreshLinkState *rrs;
1479 :
1480 0 : rrs = GNUNET_new (struct RefreshLinkState);
1481 0 : rrs->reveal_reference = reveal_reference;
1482 0 : rrs->expected_response_code = expected_response_code;
1483 : {
1484 0 : struct TALER_TESTING_Command cmd = {
1485 : .cls = rrs,
1486 : .label = label,
1487 : .run = &refresh_link_run,
1488 : .cleanup = &refresh_link_cleanup
1489 : };
1490 :
1491 0 : return cmd;
1492 : }
1493 : }
1494 :
1495 :
1496 : struct TALER_TESTING_Command
1497 0 : TALER_TESTING_cmd_refresh_link_with_retry (struct TALER_TESTING_Command cmd)
1498 : {
1499 : struct RefreshLinkState *rls;
1500 :
1501 0 : GNUNET_assert (&refresh_link_run == cmd.run);
1502 0 : rls = cmd.cls;
1503 0 : rls->do_retry = NUM_RETRIES;
1504 0 : return cmd;
1505 : }
|