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