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