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 : * @author Özgür Kesim
24 : */
25 : #include "taler/taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler/taler_testing_lib.h"
28 : #include "taler/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 : * Set (by the interpreter) to the coin's public key.
75 : */
76 : struct TALER_CoinSpendPublicKeyP coin_pub;
77 :
78 : /**
79 : * Fresh age commitment for the coin with proof and its hash, NULL if not
80 : * applicable.
81 : */
82 : struct TALER_AgeCommitmentProof *age_commitment_proof;
83 : struct TALER_AgeCommitmentHashP h_age_commitment;
84 :
85 : /**
86 : * The blinding key (needed for recoup operations).
87 : */
88 : union GNUNET_CRYPTO_BlindingSecretP blinding_key;
89 :
90 : };
91 :
92 :
93 : /**
94 : * State for a "refresh melt" command.
95 : */
96 : struct MeltState
97 : {
98 :
99 : /**
100 : * Reference to reserve_withdraw operations for coin to
101 : * be used for the /refresh/melt operation.
102 : */
103 : const char *coin_reference;
104 :
105 : /**
106 : * Our command.
107 : */
108 : const struct TALER_TESTING_Command *cmd;
109 :
110 : /**
111 : * Reference to a previous melt command.
112 : */
113 : const char *melt_reference;
114 :
115 : /**
116 : * Melt handle while operation is running.
117 : */
118 : struct TALER_EXCHANGE_PostMeltHandle *mh;
119 :
120 : /**
121 : * Expected entry in the coin history created by this
122 : * operation.
123 : */
124 : struct TALER_EXCHANGE_CoinHistoryEntry che;
125 :
126 : /**
127 : * Interpreter state.
128 : */
129 : struct TALER_TESTING_Interpreter *is;
130 :
131 : /**
132 : * The input for the call to /melt
133 : */
134 : struct TALER_EXCHANGE_MeltInput melt_input;
135 :
136 : /**
137 : * Length of the @a blinding_values array with the exchange values
138 : * and blinding keys we are using.
139 : */
140 : unsigned int num_blinding_values;
141 :
142 : /**
143 : * Blinding values returned per coin.
144 : */
145 : struct TALER_ExchangeBlindingValues *blinding_values;
146 :
147 : /**
148 : * The input for the call to /reveal-melt
149 : */
150 : struct TALER_EXCHANGE_RevealMeltInput reveal_melt_input;
151 :
152 : /**
153 : * Array of the denomination public keys
154 : * corresponding to the @e num_fresh_coins;
155 : */
156 : struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
157 :
158 : /**
159 : * Private key of the dirty coin being melted.
160 : */
161 : const struct TALER_CoinSpendPrivateKeyP *melt_priv;
162 :
163 : /**
164 : * Public key of the dirty coin being melted.
165 : */
166 : struct TALER_CoinSpendPublicKeyP melt_pub;
167 :
168 : /**
169 : * Entropy seed for the refresh-melt operation.
170 : */
171 : struct TALER_PublicRefreshMasterSeedP rms;
172 :
173 : /**
174 : * If false, @e blinding_seed contains the seed for the
175 : * blinding values for CS signatures
176 : */
177 : bool no_blinding_seed;
178 :
179 : /**
180 : * If @e no_blinding_seed is false, contains the blinding
181 : * seed from which the nonces were derived for CS signatures
182 : */
183 : struct TALER_BlindingMasterSeedP blinding_seed;
184 :
185 : /**
186 : * The refresh commitment we calculated
187 : */
188 : struct TALER_RefreshCommitmentP rc;
189 :
190 : /**
191 : * The kappa refresh nonces for signing with the old coin.
192 : */
193 : struct TALER_KappaPublicRefreshNoncesP kappa_nonces;
194 :
195 : /**
196 : * Task scheduled to try later.
197 : */
198 : struct GNUNET_SCHEDULER_Task *retry_task;
199 :
200 : /**
201 : * How long do we wait until we retry?
202 : */
203 : struct GNUNET_TIME_Relative backoff;
204 :
205 : /**
206 : * How long did we wait in total for retries?
207 : */
208 : struct GNUNET_TIME_Relative total_backoff;
209 :
210 : /**
211 : * Amounts to be generated during melt.
212 : */
213 : const char **melt_fresh_amounts;
214 :
215 : /**
216 : * Number of fresh coins generated by the melt.
217 : */
218 : unsigned int num_fresh_coins;
219 :
220 : /**
221 : * Expected HTTP response code.
222 : */
223 : unsigned int expected_response_code;
224 :
225 : /**
226 : * if set to #GNUNET_YES, then two /refresh/melt operations
227 : * will be performed. This is needed to trigger the logic
228 : * that manages those already-made requests. Note: it
229 : * is not possible to just copy-and-paste a test refresh melt
230 : * CMD to have the same effect, because every data preparation
231 : * generates new planchets that (in turn) make the whole "hash"
232 : * different from any previous one, therefore NOT allowing the
233 : * exchange to pick any previous /rerfesh/melt operation from
234 : * the database.
235 : */
236 : bool double_melt;
237 :
238 : /**
239 : * How often should we retry on (transient) failures?
240 : */
241 : unsigned int do_retry;
242 :
243 : /**
244 : * Set by the melt callback as it comes from the exchange.
245 : */
246 : uint16_t noreveal_index;
247 :
248 : /**
249 : * The signatures over the nonces we need to reveal
250 : */
251 : struct TALER_RevealPrivateRefreshNonceSignaturesP revealed_signatures;
252 :
253 : };
254 :
255 :
256 : /**
257 : * State for a "refresh reveal" CMD.
258 : */
259 : struct RevealMeltState
260 : {
261 : /**
262 : * Link to a "refresh melt" command.
263 : */
264 : const char *melt_reference;
265 :
266 : /**
267 : * Reveal handle while operation is running.
268 : */
269 : struct TALER_EXCHANGE_PostRevealMeltHandle *rmh;
270 :
271 : /**
272 : * Our command.
273 : */
274 : const struct TALER_TESTING_Command *cmd;
275 :
276 : /**
277 : * Convenience struct to keep in one place all the
278 : * data related to one fresh coin, set by the reveal callback
279 : * as it comes from the exchange.
280 : */
281 : struct TALER_TESTING_FreshCoinData *fresh_coins;
282 :
283 : /**
284 : * Array of @e num_fresh_coins planchet secrets derived
285 : * from the transfer secret per fresh coin.
286 : */
287 : struct TALER_PlanchetMasterSecretP *psa;
288 :
289 : /**
290 : * Interpreter state.
291 : */
292 : struct TALER_TESTING_Interpreter *is;
293 :
294 : /**
295 : * Task scheduled to try later.
296 : */
297 : struct GNUNET_SCHEDULER_Task *retry_task;
298 :
299 : /**
300 : * How long do we wait until we retry?
301 : */
302 : struct GNUNET_TIME_Relative backoff;
303 :
304 : /**
305 : * How long did we wait in total for retries?
306 : */
307 : struct GNUNET_TIME_Relative total_backoff;
308 :
309 : /**
310 : * Number of fresh coins withdrawn, set by the
311 : * reveal callback as it comes from the exchange,
312 : * it is the length of the @e fresh_coins array.
313 : */
314 : unsigned int num_fresh_coins;
315 :
316 : /**
317 : * Expected HTTP response code.
318 : */
319 : unsigned int expected_response_code;
320 :
321 : /**
322 : * How often should we retry on (transient) failures?
323 : */
324 : unsigned int do_retry;
325 :
326 : };
327 :
328 :
329 : /**
330 : * State for a "refresh link" CMD.
331 : */
332 : struct RefreshLinkState
333 : {
334 : /**
335 : * Link to a "refresh reveal" command.
336 : */
337 : const char *reveal_reference;
338 :
339 : /**
340 : * Our command.
341 : */
342 : const struct TALER_TESTING_Command *cmd;
343 :
344 : /**
345 : * Handle to the ongoing operation.
346 : */
347 : struct TALER_EXCHANGE_LinkHandle *rlh;
348 :
349 : /**
350 : * Interpreter state.
351 : */
352 : struct TALER_TESTING_Interpreter *is;
353 :
354 : /**
355 : * Task scheduled to try later.
356 : */
357 : struct GNUNET_SCHEDULER_Task *retry_task;
358 :
359 : /**
360 : * How long do we wait until we retry?
361 : */
362 : struct GNUNET_TIME_Relative backoff;
363 :
364 : /**
365 : * How long did we wait in total for retries?
366 : */
367 : struct GNUNET_TIME_Relative total_backoff;
368 :
369 : /**
370 : * Expected HTTP response code.
371 : */
372 : unsigned int expected_response_code;
373 :
374 : /**
375 : * How often should we retry on (transient) failures?
376 : */
377 : unsigned int do_retry;
378 :
379 : };
380 :
381 :
382 : /**
383 : * Run the command.
384 : *
385 : * @param cls closure.
386 : * @param cmd the command to execute.
387 : * @param is the interpreter state.
388 : */
389 : static void
390 : melt_reveal_run (void *cls,
391 : const struct TALER_TESTING_Command *cmd,
392 : struct TALER_TESTING_Interpreter *is);
393 :
394 :
395 : /**
396 : * Task scheduled to re-try #melt_reveal_run.
397 : *
398 : * @param cls a `struct RefreshRevealState`
399 : */
400 : static void
401 0 : do_reveal_retry (void *cls)
402 : {
403 0 : struct RevealMeltState *rrs = cls;
404 :
405 0 : rrs->retry_task = NULL;
406 0 : TALER_TESTING_touch_cmd (rrs->is);
407 0 : melt_reveal_run (rrs,
408 : NULL,
409 : rrs->is);
410 0 : }
411 :
412 :
413 : /**
414 : * "refresh reveal" request callback; it checks that the response
415 : * code is expected and copies into its command's state the data
416 : * coming from the exchange, namely the fresh coins.
417 : *
418 : * @param cls closure, a `struct RevealMeltState`
419 : * @param rmr HTTP response details
420 : */
421 : static void
422 14 : reveal_cb (void *cls,
423 : const struct TALER_EXCHANGE_PostRevealMeltResponse *rmr)
424 : {
425 14 : struct RevealMeltState *rrs = cls;
426 14 : const struct TALER_EXCHANGE_HttpResponse *hr = &rmr->hr;
427 : const struct TALER_TESTING_Command *melt_cmd;
428 :
429 14 : rrs->rmh = NULL;
430 14 : if (rrs->expected_response_code != hr->http_status)
431 : {
432 0 : if (0 != rrs->do_retry)
433 : {
434 0 : rrs->do_retry--;
435 0 : if ( (0 == hr->http_status) ||
436 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
437 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
438 : {
439 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
440 : "Retrying refresh reveal failed with %u/%d\n",
441 : hr->http_status,
442 : (int) hr->ec);
443 : /* on DB conflicts, do not use backoff */
444 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
445 0 : rrs->backoff = GNUNET_TIME_UNIT_ZERO;
446 : else
447 0 : rrs->backoff = GNUNET_TIME_randomized_backoff (rrs->backoff,
448 : MAX_BACKOFF);
449 0 : rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff,
450 : rrs->backoff);
451 0 : TALER_TESTING_inc_tries (rrs->is);
452 0 : rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff,
453 : &do_reveal_retry,
454 : rrs);
455 0 : return;
456 : }
457 : }
458 0 : TALER_TESTING_unexpected_status_with_body (rrs->is,
459 : hr->http_status,
460 : rrs->expected_response_code,
461 : hr->reply);
462 0 : return;
463 : }
464 14 : melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is,
465 : rrs->melt_reference);
466 14 : if (NULL == melt_cmd)
467 : {
468 0 : GNUNET_break (0);
469 0 : TALER_TESTING_interpreter_fail (rrs->is);
470 0 : return;
471 : }
472 14 : switch (hr->http_status)
473 : {
474 14 : case MHD_HTTP_OK:
475 14 : rrs->num_fresh_coins = rmr->details.ok.num_coins;
476 14 : rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
477 : struct TALER_PlanchetMasterSecretP);
478 14 : rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
479 : struct TALER_TESTING_FreshCoinData);
480 70 : for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
481 : {
482 56 : const struct TALER_EXCHANGE_RevealedCoinInfo *coin
483 56 : = &rmr->details.ok.coins[i];
484 56 : struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
485 :
486 56 : rrs->psa[i] = coin->ps;
487 56 : fc->blinding_key = coin->bks;
488 56 : if (GNUNET_OK !=
489 56 : TALER_TESTING_get_trait_denom_pub (melt_cmd,
490 : i,
491 : &fc->pk))
492 : {
493 0 : GNUNET_break (0);
494 0 : TALER_TESTING_interpreter_fail (rrs->is);
495 0 : return;
496 : }
497 56 : fc->coin_priv = coin->coin_priv;
498 56 : GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
499 : &fc->coin_pub.eddsa_pub);
500 :
501 56 : if (NULL != coin->age_commitment_proof)
502 : {
503 32 : fc->age_commitment_proof =
504 32 : TALER_age_commitment_proof_duplicate (coin->age_commitment_proof);
505 32 : fc->h_age_commitment = coin->h_age_commitment;
506 : }
507 :
508 56 : TALER_denom_sig_copy (&fc->sig,
509 : &coin->sig);
510 : }
511 14 : if (0 != rrs->total_backoff.rel_value_us)
512 : {
513 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
514 : "Total reveal backoff for %s was %s\n",
515 : rrs->cmd->label,
516 : GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff,
517 : true));
518 : }
519 14 : break;
520 0 : default:
521 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
522 : "Unknown HTTP status %u/%d\n",
523 : hr->http_status,
524 : (int) hr->ec);
525 : }
526 14 : TALER_TESTING_interpreter_next (rrs->is);
527 : }
528 :
529 :
530 : /**
531 : * Run the command.
532 : *
533 : * @param cls closure.
534 : * @param cmd the command to execute.
535 : * @param is the interpreter state.
536 : */
537 : static void
538 : melt_run (void *cls,
539 : const struct TALER_TESTING_Command *cmd,
540 : struct TALER_TESTING_Interpreter *is);
541 :
542 :
543 : /**
544 : * Run the command.
545 : *
546 : * @param cls closure.
547 : * @param cmd the command to execute.
548 : * @param is the interpreter state.
549 : */
550 : static void
551 14 : melt_reveal_run (void *cls,
552 : const struct TALER_TESTING_Command *cmd,
553 : struct TALER_TESTING_Interpreter *is)
554 : {
555 14 : struct RevealMeltState *rrs = cls;
556 : struct MeltState *ms;
557 : const struct TALER_TESTING_Command *melt_cmd;
558 :
559 14 : rrs->cmd = cmd;
560 14 : rrs->is = is;
561 14 : melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
562 : rrs->melt_reference);
563 14 : if (NULL == melt_cmd)
564 : {
565 0 : GNUNET_break (0);
566 0 : TALER_TESTING_interpreter_fail (rrs->is);
567 0 : return;
568 : }
569 14 : GNUNET_assert (melt_cmd->run == &melt_run);
570 14 : ms = melt_cmd->cls;
571 14 : ms->reveal_melt_input.rms = &ms->rms;
572 14 : ms->reveal_melt_input.melt_input = &ms->melt_input;
573 28 : ms->reveal_melt_input.blinding_seed = ms->no_blinding_seed
574 : ? NULL
575 14 : : &ms->blinding_seed;
576 14 : ms->reveal_melt_input.num_blinding_values = ms->num_blinding_values;
577 14 : ms->reveal_melt_input.blinding_values = ms->blinding_values;
578 14 : ms->reveal_melt_input.noreveal_index = ms->noreveal_index;
579 14 : rrs->rmh = TALER_EXCHANGE_post_reveal_melt_create (
580 : TALER_TESTING_interpreter_get_context (is),
581 : TALER_TESTING_get_exchange_url (is),
582 14 : &ms->reveal_melt_input);
583 14 : if (NULL == rrs->rmh)
584 : {
585 0 : GNUNET_break (0);
586 0 : TALER_TESTING_interpreter_fail (is);
587 0 : return;
588 : }
589 14 : GNUNET_assert (TALER_EC_NONE ==
590 : TALER_EXCHANGE_post_reveal_melt_start (rrs->rmh,
591 : &reveal_cb,
592 : rrs));
593 : }
594 :
595 :
596 : /**
597 : * Free the state from a "refresh reveal" CMD, and possibly
598 : * cancel a pending operation thereof.
599 : *
600 : * @param cls closure.
601 : * @param cmd the command which is being cleaned up.
602 : */
603 : static void
604 14 : melt_reveal_cleanup (void *cls,
605 : const struct TALER_TESTING_Command *cmd)
606 : {
607 14 : struct RevealMeltState *rrs = cls;
608 :
609 : (void) cmd;
610 14 : if (NULL != rrs->rmh)
611 : {
612 0 : TALER_TESTING_command_incomplete (rrs->is,
613 : cmd->label);
614 0 : TALER_EXCHANGE_post_reveal_melt_cancel (rrs->rmh);
615 0 : rrs->rmh = NULL;
616 : }
617 14 : if (NULL != rrs->retry_task)
618 : {
619 0 : GNUNET_SCHEDULER_cancel (rrs->retry_task);
620 0 : rrs->retry_task = NULL;
621 : }
622 :
623 70 : for (unsigned int j = 0; j < rrs->num_fresh_coins; j++)
624 : {
625 56 : TALER_denom_sig_free (&rrs->fresh_coins[j].sig);
626 56 : TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof);
627 56 : GNUNET_free (rrs->fresh_coins[j].age_commitment_proof);
628 : }
629 14 : GNUNET_free (rrs->fresh_coins);
630 14 : GNUNET_free (rrs->psa);
631 14 : rrs->num_fresh_coins = 0;
632 14 : GNUNET_free (rrs);
633 14 : }
634 :
635 :
636 : /**
637 : * Task scheduled to re-try #melt_run.
638 : *
639 : * @param cls a `struct RefreshMeltState`
640 : */
641 : static void
642 0 : do_melt_retry (void *cls)
643 : {
644 0 : struct MeltState *rms = cls;
645 :
646 0 : rms->retry_task = NULL;
647 0 : TALER_TESTING_touch_cmd (rms->is);
648 0 : melt_run (rms,
649 : NULL,
650 : rms->is);
651 0 : }
652 :
653 :
654 : /**
655 : * Callback for a " /melt" operation; checks if the HTTP
656 : * response code is okay and re-run the melt operation if the
657 : * CMD was set to do so.
658 : *
659 : * @param cls closure.
660 : * @param mr melt response details
661 : */
662 : static void
663 30 : melt_cb (void *cls,
664 : const struct TALER_EXCHANGE_PostMeltResponse *mr)
665 : {
666 30 : struct MeltState *ms = cls;
667 30 : const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
668 :
669 30 : ms->mh = NULL;
670 30 : if (ms->expected_response_code != hr->http_status)
671 : {
672 0 : if (0 != ms->do_retry)
673 : {
674 0 : ms->do_retry--;
675 0 : if ( (0 == hr->http_status) ||
676 0 : (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
677 0 : (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
678 : {
679 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
680 : "Retrying refresh melt failed with %u/%d\n",
681 : hr->http_status,
682 : (int) hr->ec);
683 : /* on DB conflicts, do not use backoff */
684 0 : if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
685 0 : ms->backoff = GNUNET_TIME_UNIT_ZERO;
686 : else
687 0 : ms->backoff = GNUNET_TIME_randomized_backoff (ms->backoff,
688 : MAX_BACKOFF);
689 0 : ms->total_backoff = GNUNET_TIME_relative_add (ms->total_backoff,
690 : ms->backoff);
691 0 : TALER_TESTING_inc_tries (ms->is);
692 0 : ms->retry_task = GNUNET_SCHEDULER_add_delayed (ms->backoff,
693 : &do_melt_retry,
694 : ms);
695 0 : return;
696 : }
697 : }
698 0 : TALER_TESTING_unexpected_status_with_body (ms->is,
699 : hr->http_status,
700 : ms->expected_response_code,
701 : hr->reply);
702 0 : return;
703 : }
704 30 : if (MHD_HTTP_OK == hr->http_status)
705 : {
706 16 : ms->noreveal_index = mr->details.ok.noreveal_index;
707 16 : if (mr->details.ok.num_melt_blinding_values != ms->num_fresh_coins)
708 : {
709 0 : GNUNET_break (0);
710 0 : TALER_TESTING_interpreter_fail (ms->is);
711 0 : return;
712 : }
713 16 : ms->no_blinding_seed = (NULL == mr->details.ok.blinding_seed);
714 16 : if (NULL != mr->details.ok.blinding_seed)
715 8 : ms->blinding_seed = *mr->details.ok.blinding_seed;
716 16 : ms->num_blinding_values = mr->details.ok.num_melt_blinding_values;
717 16 : if (NULL != ms->blinding_values)
718 : {
719 8 : GNUNET_break (0); /* can this this happen? Check! */
720 40 : for (unsigned int i = 0; i < ms->num_blinding_values; i++)
721 32 : TALER_denom_ewv_free (&ms->blinding_values[i]);
722 8 : GNUNET_free (ms->blinding_values);
723 : }
724 16 : ms->blinding_values = GNUNET_new_array (
725 : ms->num_blinding_values,
726 : struct TALER_ExchangeBlindingValues);
727 80 : for (unsigned int i = 0; i<ms->num_blinding_values; i++)
728 : {
729 64 : TALER_denom_ewv_copy (&ms->blinding_values[i],
730 64 : &mr->details.ok.melt_blinding_values[i]);
731 : }
732 : }
733 30 : if (0 != ms->total_backoff.rel_value_us)
734 : {
735 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
736 : "Total melt backoff for %s was %s\n",
737 : ms->cmd->label,
738 : GNUNET_STRINGS_relative_time_to_string (ms->total_backoff,
739 : true));
740 : }
741 30 : if (ms->double_melt)
742 : {
743 8 : TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
744 : ms->cmd->label);
745 8 : ms->mh = TALER_EXCHANGE_post_melt_create (
746 : TALER_TESTING_interpreter_get_context (ms->is),
747 : TALER_TESTING_get_exchange_url (ms->is),
748 : TALER_TESTING_get_keys (ms->is),
749 8 : &ms->rms,
750 8 : &ms->melt_input);
751 8 : GNUNET_assert (NULL != ms->mh);
752 8 : GNUNET_assert (TALER_EC_NONE ==
753 : TALER_EXCHANGE_post_melt_start (ms->mh,
754 : &melt_cb,
755 : ms));
756 8 : ms->double_melt = false;
757 8 : return;
758 : }
759 22 : TALER_TESTING_interpreter_next (ms->is);
760 : }
761 :
762 :
763 : /**
764 : * Run the command.
765 : *
766 : * @param cls closure.
767 : * @param cmd the command to execute.
768 : * @param is the interpreter state.
769 : */
770 : static void
771 22 : melt_run (void *cls,
772 : const struct TALER_TESTING_Command *cmd,
773 : struct TALER_TESTING_Interpreter *is)
774 : {
775 : static const char *default_melt_fresh_amounts[] = {
776 : "EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
777 : NULL
778 : };
779 22 : struct MeltState *rms = cls;
780 : unsigned int num_fresh_coins;
781 : const char **melt_fresh_amounts;
782 :
783 22 : rms->cmd = cmd;
784 22 : if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
785 22 : melt_fresh_amounts = default_melt_fresh_amounts;
786 22 : rms->is = is;
787 22 : rms->noreveal_index = UINT16_MAX;
788 22 : TALER_refresh_master_setup_random (&rms->rms);
789 22 : for (num_fresh_coins = 0;
790 110 : NULL != melt_fresh_amounts[num_fresh_coins];
791 88 : num_fresh_coins++)
792 : ;
793 22 : rms->num_fresh_coins = num_fresh_coins;
794 : /* Free old data structure in case this is a retry! */
795 22 : if (NULL != rms->fresh_pks)
796 : {
797 0 : for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
798 0 : TALER_denom_pub_free (&rms->fresh_pks[i].key);
799 0 : GNUNET_free (rms->fresh_pks);
800 : }
801 22 : rms->fresh_pks = GNUNET_new_array (
802 : num_fresh_coins,
803 : struct TALER_EXCHANGE_DenomPublicKey);
804 : {
805 : struct TALER_Amount melt_amount;
806 : struct TALER_Amount fresh_amount;
807 22 : const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
808 22 : const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL;
809 : const struct TALER_DenominationSignature *melt_sig;
810 : const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
811 : const struct TALER_TESTING_Command *coin_command;
812 : bool age_restricted_denom;
813 :
814 22 : if (NULL == (coin_command
815 22 : = TALER_TESTING_interpreter_lookup_command (
816 : is,
817 : rms->coin_reference)))
818 : {
819 0 : GNUNET_break (0);
820 0 : TALER_TESTING_interpreter_fail (rms->is);
821 0 : return;
822 : }
823 :
824 22 : if (GNUNET_OK !=
825 22 : TALER_TESTING_get_trait_coin_priv (coin_command,
826 : 0,
827 : &rms->melt_priv))
828 : {
829 0 : GNUNET_break (0);
830 0 : TALER_TESTING_interpreter_fail (rms->is);
831 0 : return;
832 : }
833 22 : if (GNUNET_OK !=
834 22 : TALER_TESTING_get_trait_age_commitment_proof (coin_command,
835 : 0,
836 : &age_commitment_proof))
837 : {
838 0 : GNUNET_break (0);
839 0 : TALER_TESTING_interpreter_fail (rms->is);
840 0 : return;
841 : }
842 :
843 22 : if (GNUNET_OK !=
844 22 : TALER_TESTING_get_trait_h_age_commitment (coin_command,
845 : 0,
846 : &h_age_commitment))
847 : {
848 0 : GNUNET_break (0);
849 0 : TALER_TESTING_interpreter_fail (rms->is);
850 0 : return;
851 : }
852 22 : if (GNUNET_OK !=
853 22 : TALER_TESTING_get_trait_denom_sig (coin_command,
854 : 0,
855 : &melt_sig))
856 : {
857 0 : GNUNET_break (0);
858 0 : TALER_TESTING_interpreter_fail (rms->is);
859 0 : return;
860 : }
861 22 : if (GNUNET_OK !=
862 22 : TALER_TESTING_get_trait_denom_pub (coin_command,
863 : 0,
864 : &melt_denom_pub))
865 : {
866 0 : GNUNET_break (0);
867 0 : TALER_TESTING_interpreter_fail (rms->is);
868 0 : return;
869 : }
870 :
871 : /* Melt amount starts with the melt fee of the old coin; we'll add the
872 : values and withdraw fees of the fresh coins next */
873 22 : melt_amount = melt_denom_pub->fees.refresh;
874 22 : age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0;
875 22 : GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof));
876 22 : GNUNET_assert ((NULL == age_commitment_proof) ||
877 : (0 < age_commitment_proof->commitment.num));
878 110 : for (unsigned int i = 0; i<num_fresh_coins; i++)
879 : {
880 : const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
881 :
882 88 : if (GNUNET_OK !=
883 88 : TALER_string_to_amount (melt_fresh_amounts[i],
884 : &fresh_amount))
885 : {
886 0 : GNUNET_break (0);
887 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
888 : "Failed to parse amount `%s' at index %u\n",
889 : melt_fresh_amounts[i],
890 : i);
891 0 : TALER_TESTING_interpreter_fail (rms->is);
892 0 : return;
893 : }
894 88 : fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is),
895 : &fresh_amount,
896 : age_restricted_denom);
897 88 : if (NULL == fresh_pk)
898 : {
899 0 : GNUNET_break (0);
900 : /* Subroutine logs specific error */
901 0 : TALER_TESTING_interpreter_fail (rms->is);
902 0 : return;
903 : }
904 88 : GNUNET_assert (0 <=
905 : TALER_amount_add (&melt_amount,
906 : &melt_amount,
907 : &fresh_amount));
908 88 : GNUNET_assert (0 <=
909 : TALER_amount_add (&melt_amount,
910 : &melt_amount,
911 : &fresh_pk->fees.withdraw));
912 88 : rms->fresh_pks[i] = *fresh_pk;
913 : /* Make a deep copy of the RSA key */
914 88 : TALER_denom_pub_copy (&rms->fresh_pks[i].key,
915 : &fresh_pk->key);
916 : } /* end for */
917 :
918 22 : rms->melt_input.melt_priv = *rms->melt_priv;
919 22 : GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv,
920 : &rms->melt_pub.eddsa_pub);
921 22 : rms->melt_input.melt_amount = melt_amount;
922 22 : rms->melt_input.melt_sig = *melt_sig;
923 22 : rms->melt_input.melt_pk = *melt_denom_pub;
924 :
925 22 : if (NULL != age_commitment_proof)
926 : {
927 12 : GNUNET_assert (NULL != h_age_commitment);
928 12 : rms->melt_input.melt_age_commitment_proof = age_commitment_proof;
929 12 : rms->melt_input.melt_h_age_commitment = h_age_commitment;
930 : }
931 22 : rms->melt_input.fresh_denom_pubs = rms->fresh_pks;
932 22 : rms->melt_input.num_fresh_denom_pubs = num_fresh_coins;
933 :
934 22 : GNUNET_assert (age_restricted_denom ==
935 : (NULL != age_commitment_proof));
936 22 : GNUNET_assert ((NULL == age_commitment_proof) ||
937 : (0 < age_commitment_proof->commitment.num));
938 :
939 22 : rms->che.type = TALER_EXCHANGE_CTT_MELT;
940 22 : rms->che.amount = melt_amount;
941 22 : if (NULL != age_commitment_proof)
942 12 : rms->che.details.melt.h_age_commitment = *h_age_commitment;
943 : else
944 10 : rms->che.details.melt.no_hac = true;
945 :
946 22 : rms->mh = TALER_EXCHANGE_post_melt_create (
947 : TALER_TESTING_interpreter_get_context (is),
948 : TALER_TESTING_get_exchange_url (is),
949 : TALER_TESTING_get_keys (is),
950 22 : &rms->rms,
951 22 : &rms->melt_input);
952 :
953 22 : if (NULL == rms->mh)
954 : {
955 0 : GNUNET_break (0);
956 0 : TALER_TESTING_interpreter_fail (rms->is);
957 0 : return;
958 : }
959 22 : GNUNET_assert (TALER_EC_NONE ==
960 : TALER_EXCHANGE_post_melt_start (rms->mh,
961 : &melt_cb,
962 : rms));
963 : }
964 : }
965 :
966 :
967 : /**
968 : * Free the "refresh melt" CMD state, and possibly cancel a
969 : * pending operation thereof.
970 : *
971 : * @param cls closure, must be a `struct RefreshMeltState`.
972 : * @param cmd the command which is being cleaned up.
973 : */
974 : static void
975 22 : melt_cleanup (void *cls,
976 : const struct TALER_TESTING_Command *cmd)
977 : {
978 22 : struct MeltState *rms = cls;
979 :
980 : (void) cmd;
981 22 : if (NULL != rms->mh)
982 : {
983 0 : TALER_TESTING_command_incomplete (rms->is,
984 : cmd->label);
985 0 : TALER_EXCHANGE_post_melt_cancel (rms->mh);
986 0 : rms->mh = NULL;
987 : }
988 22 : if (NULL != rms->retry_task)
989 : {
990 0 : GNUNET_SCHEDULER_cancel (rms->retry_task);
991 0 : rms->retry_task = NULL;
992 : }
993 22 : if (NULL != rms->fresh_pks)
994 : {
995 110 : for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
996 88 : TALER_denom_pub_free (&rms->fresh_pks[i].key);
997 22 : GNUNET_free (rms->fresh_pks);
998 : }
999 22 : if (NULL != rms->blinding_values)
1000 : {
1001 40 : for (unsigned int i = 0; i < rms->num_blinding_values; i++)
1002 32 : TALER_denom_ewv_free (&rms->blinding_values[i]);
1003 8 : GNUNET_free (rms->blinding_values);
1004 : }
1005 22 : GNUNET_free (rms->melt_fresh_amounts);
1006 22 : GNUNET_free (rms);
1007 22 : }
1008 :
1009 :
1010 : /**
1011 : * Offer internal data to the "refresh melt" CMD.
1012 : *
1013 : * @param cls closure.
1014 : * @param[out] ret result (could be anything).
1015 : * @param trait name of the trait.
1016 : * @param index index number of the object to offer.
1017 : * @return #GNUNET_OK on success.
1018 : */
1019 : static enum GNUNET_GenericReturnValue
1020 98 : melt_traits (void *cls,
1021 : const void **ret,
1022 : const char *trait,
1023 : unsigned int index)
1024 : {
1025 98 : struct MeltState *rms = cls;
1026 :
1027 98 : if (index >= rms->num_fresh_coins)
1028 : {
1029 0 : GNUNET_break (0);
1030 0 : return GNUNET_SYSERR;
1031 : }
1032 : {
1033 : struct TALER_TESTING_Trait traits[] = {
1034 98 : TALER_TESTING_make_trait_denom_pub (index,
1035 98 : &rms->fresh_pks[index]),
1036 98 : TALER_TESTING_make_trait_coin_priv (0,
1037 : rms->melt_priv),
1038 98 : TALER_TESTING_make_trait_coin_pub (0,
1039 98 : &rms->melt_pub),
1040 98 : TALER_TESTING_make_trait_coin_history (0,
1041 98 : &rms->che),
1042 98 : TALER_TESTING_make_trait_age_commitment_proof (
1043 : index,
1044 : rms->melt_input.melt_age_commitment_proof),
1045 98 : TALER_TESTING_make_trait_h_age_commitment (
1046 : index,
1047 : rms->melt_input.melt_h_age_commitment),
1048 98 : TALER_TESTING_make_trait_refresh_seed (&rms->rms),
1049 98 : (NULL != rms->reveal_melt_input.blinding_values)
1050 68 : ? TALER_TESTING_make_trait_exchange_blinding_values (
1051 : index,
1052 68 : &rms->reveal_melt_input.blinding_values[index])
1053 98 : : TALER_TESTING_trait_end (),
1054 98 : TALER_TESTING_trait_end ()
1055 : };
1056 :
1057 98 : return TALER_TESTING_get_trait (traits,
1058 : ret,
1059 : trait,
1060 : index);
1061 : }
1062 : }
1063 :
1064 :
1065 : /**
1066 : * Parse list of amounts for melt operation.
1067 : *
1068 : * @param[in,out] rms where to store the list
1069 : * @param ap NULL-termianted list of amounts to be melted (one per fresh coin)
1070 : * @return #GNUNET_OK on success
1071 : */
1072 : static enum GNUNET_GenericReturnValue
1073 22 : parse_amounts (struct MeltState *rms,
1074 : va_list ap)
1075 : {
1076 : unsigned int len;
1077 : unsigned int off;
1078 : const char *amount;
1079 :
1080 22 : len = 0;
1081 22 : off = 0;
1082 44 : while (NULL != (amount = va_arg (ap, const char *)))
1083 : {
1084 0 : if (len == off)
1085 : {
1086 : struct TALER_Amount a;
1087 :
1088 0 : GNUNET_array_grow (rms->melt_fresh_amounts,
1089 : len,
1090 : off + 16);
1091 0 : if (GNUNET_OK !=
1092 0 : TALER_string_to_amount (amount, &a))
1093 : {
1094 0 : GNUNET_break (0);
1095 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1096 : "Failed to parse amount `%s' at index %u\n",
1097 : amount, off);
1098 0 : GNUNET_free (rms->melt_fresh_amounts);
1099 0 : rms->melt_fresh_amounts = NULL;
1100 0 : return GNUNET_SYSERR;
1101 : }
1102 0 : rms->melt_fresh_amounts[off++] = amount;
1103 : }
1104 : }
1105 22 : if (0 == off)
1106 22 : return GNUNET_OK; /* no amounts given == use defaults! */
1107 : /* ensure NULL-termination */
1108 0 : GNUNET_array_grow (rms->melt_fresh_amounts,
1109 : len,
1110 : off + 1);
1111 0 : return GNUNET_OK;
1112 : }
1113 :
1114 :
1115 : struct TALER_TESTING_Command
1116 14 : TALER_TESTING_cmd_melt (const char *label,
1117 : const char *coin_reference,
1118 : unsigned int expected_response_code,
1119 : ...)
1120 : {
1121 : struct MeltState *rms;
1122 : va_list ap;
1123 :
1124 14 : rms = GNUNET_new (struct MeltState);
1125 14 : rms->coin_reference = coin_reference;
1126 14 : rms->expected_response_code = expected_response_code;
1127 14 : va_start (ap,
1128 : expected_response_code);
1129 14 : GNUNET_assert (GNUNET_OK ==
1130 : parse_amounts (rms, ap));
1131 14 : va_end (ap);
1132 : {
1133 14 : struct TALER_TESTING_Command cmd = {
1134 : .label = label,
1135 : .cls = rms,
1136 : .run = &melt_run,
1137 : .cleanup = &melt_cleanup,
1138 : .traits = &melt_traits
1139 : };
1140 :
1141 14 : return cmd;
1142 : }
1143 : }
1144 :
1145 :
1146 : struct TALER_TESTING_Command
1147 8 : TALER_TESTING_cmd_melt_double (const char *label,
1148 : const char *coin_reference,
1149 : unsigned int expected_response_code,
1150 : ...)
1151 : {
1152 : struct MeltState *rms;
1153 : va_list ap;
1154 :
1155 8 : rms = GNUNET_new (struct MeltState);
1156 8 : rms->coin_reference = coin_reference;
1157 8 : rms->expected_response_code = expected_response_code;
1158 8 : rms->double_melt = true;
1159 8 : va_start (ap,
1160 : expected_response_code);
1161 8 : GNUNET_assert (GNUNET_OK ==
1162 : parse_amounts (rms, ap));
1163 8 : va_end (ap);
1164 : {
1165 8 : struct TALER_TESTING_Command cmd = {
1166 : .label = label,
1167 : .cls = rms,
1168 : .run = &melt_run,
1169 : .cleanup = &melt_cleanup,
1170 : .traits = &melt_traits
1171 : };
1172 :
1173 8 : return cmd;
1174 : }
1175 : }
1176 :
1177 :
1178 : struct TALER_TESTING_Command
1179 0 : TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
1180 : {
1181 : struct MeltState *rms;
1182 :
1183 0 : GNUNET_assert (&melt_run == cmd.run);
1184 0 : rms = cmd.cls;
1185 0 : rms->do_retry = NUM_RETRIES;
1186 0 : return cmd;
1187 : }
1188 :
1189 :
1190 : /**
1191 : * Offer internal data from a "refresh reveal" CMD.
1192 : *
1193 : * @param cls closure.
1194 : * @param[out] ret result (could be anything).
1195 : * @param trait name of the trait.
1196 : * @param index index number of the object to offer.
1197 : * @return #GNUNET_OK on success.
1198 : */
1199 : static enum GNUNET_GenericReturnValue
1200 230 : melt_reveal_traits (void *cls,
1201 : const void **ret,
1202 : const char *trait,
1203 : unsigned int index)
1204 : {
1205 230 : struct RevealMeltState *rrs = cls;
1206 :
1207 230 : if (index >= rrs->num_fresh_coins)
1208 8 : return GNUNET_SYSERR;
1209 :
1210 : {
1211 : struct TALER_TESTING_Trait traits[] = {
1212 222 : TALER_TESTING_make_trait_coin_priv (
1213 : index,
1214 222 : &rrs->fresh_coins[index].coin_priv),
1215 222 : TALER_TESTING_make_trait_coin_pub (
1216 : index,
1217 222 : &rrs->fresh_coins[index].coin_pub),
1218 222 : TALER_TESTING_make_trait_age_commitment_proof (
1219 : index,
1220 222 : rrs->fresh_coins[index].age_commitment_proof),
1221 222 : TALER_TESTING_make_trait_h_age_commitment (
1222 : index,
1223 222 : &rrs->fresh_coins[index].h_age_commitment),
1224 222 : TALER_TESTING_make_trait_denom_pub (
1225 : index,
1226 222 : rrs->fresh_coins[index].pk),
1227 222 : TALER_TESTING_make_trait_denom_sig (
1228 : index,
1229 222 : &rrs->fresh_coins[index].sig),
1230 222 : TALER_TESTING_make_trait_blinding_key (
1231 : index,
1232 222 : &rrs->fresh_coins[index].blinding_key),
1233 222 : TALER_TESTING_make_trait_array_length (
1234 222 : &rrs->num_fresh_coins),
1235 222 : TALER_TESTING_make_trait_fresh_coins (
1236 222 : (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins),
1237 222 : TALER_TESTING_make_trait_planchet_secrets (index,
1238 222 : &rrs->psa[index]),
1239 222 : TALER_TESTING_trait_end ()
1240 : };
1241 :
1242 222 : return TALER_TESTING_get_trait (traits,
1243 : ret,
1244 : trait,
1245 : index);
1246 : }
1247 : }
1248 :
1249 :
1250 : struct TALER_TESTING_Command
1251 14 : TALER_TESTING_cmd_melt_reveal (const char *label,
1252 : const char *melt_reference,
1253 : unsigned int expected_response_code)
1254 : {
1255 : struct RevealMeltState *rrs;
1256 :
1257 14 : rrs = GNUNET_new (struct RevealMeltState);
1258 14 : rrs->melt_reference = melt_reference;
1259 14 : rrs->expected_response_code = expected_response_code;
1260 : {
1261 14 : struct TALER_TESTING_Command cmd = {
1262 : .cls = rrs,
1263 : .label = label,
1264 : .run = &melt_reveal_run,
1265 : .cleanup = &melt_reveal_cleanup,
1266 : .traits = &melt_reveal_traits
1267 : };
1268 :
1269 14 : return cmd;
1270 : }
1271 : }
1272 :
1273 :
1274 : struct TALER_TESTING_Command
1275 0 : TALER_TESTING_cmd_melt_reveal_with_retry (struct TALER_TESTING_Command cmd)
1276 : {
1277 : struct RevealMeltState *rrs;
1278 :
1279 0 : GNUNET_assert (&melt_reveal_run == cmd.run);
1280 0 : rrs = cmd.cls;
1281 0 : rrs->do_retry = NUM_RETRIES;
1282 0 : return cmd;
1283 : }
|