testing_api_cmd_refresh.c (36359B)
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 do_reveal_retry (void *cls) 408 { 409 struct RevealMeltState *rrs = cls; 410 411 rrs->retry_task = NULL; 412 TALER_TESTING_touch_cmd (rrs->is); 413 melt_reveal_run (rrs, 414 NULL, 415 rrs->is); 416 } 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 reveal_cb (struct RevealMeltState *rrs, 429 const struct TALER_EXCHANGE_PostRevealMeltResponse *rmr) 430 { 431 const struct TALER_EXCHANGE_HttpResponse *hr = &rmr->hr; 432 const struct TALER_TESTING_Command *melt_cmd; 433 434 rrs->rmh = NULL; 435 if (rrs->expected_response_code != hr->http_status) 436 { 437 if (0 != rrs->do_retry) 438 { 439 rrs->do_retry--; 440 if ( (0 == hr->http_status) || 441 (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || 442 (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) 443 { 444 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 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) 450 rrs->backoff = GNUNET_TIME_UNIT_ZERO; 451 else 452 rrs->backoff = GNUNET_TIME_randomized_backoff (rrs->backoff, 453 MAX_BACKOFF); 454 rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff, 455 rrs->backoff); 456 TALER_TESTING_inc_tries (rrs->is); 457 rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff, 458 &do_reveal_retry, 459 rrs); 460 return; 461 } 462 } 463 TALER_TESTING_unexpected_status_with_body (rrs->is, 464 hr->http_status, 465 rrs->expected_response_code, 466 hr->reply); 467 return; 468 } 469 melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is, 470 rrs->melt_reference); 471 if (NULL == melt_cmd) 472 { 473 GNUNET_break (0); 474 TALER_TESTING_interpreter_fail (rrs->is); 475 return; 476 } 477 switch (hr->http_status) 478 { 479 case MHD_HTTP_OK: 480 rrs->num_fresh_coins = rmr->details.ok.num_coins; 481 rrs->psa = GNUNET_new_array (rrs->num_fresh_coins, 482 struct TALER_PlanchetMasterSecretP); 483 rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins, 484 struct TALER_TESTING_FreshCoinData); 485 for (unsigned int i = 0; i<rrs->num_fresh_coins; i++) 486 { 487 const struct TALER_EXCHANGE_RevealedCoinInfo *coin 488 = &rmr->details.ok.coins[i]; 489 struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i]; 490 491 rrs->psa[i] = coin->ps; 492 fc->blinding_key = coin->bks; 493 if (GNUNET_OK != 494 TALER_TESTING_get_trait_denom_pub (melt_cmd, 495 i, 496 &fc->pk)) 497 { 498 GNUNET_break (0); 499 TALER_TESTING_interpreter_fail (rrs->is); 500 return; 501 } 502 fc->coin_priv = coin->coin_priv; 503 GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv, 504 &fc->coin_pub.eddsa_pub); 505 506 if (NULL != coin->age_commitment_proof) 507 { 508 fc->age_commitment_proof = 509 TALER_age_commitment_proof_duplicate (coin->age_commitment_proof); 510 fc->h_age_commitment = coin->h_age_commitment; 511 } 512 513 TALER_denom_sig_copy (&fc->sig, 514 &coin->sig); 515 } 516 if (0 != rrs->total_backoff.rel_value_us) 517 { 518 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 break; 525 default: 526 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 527 "Unknown HTTP status %u/%d\n", 528 hr->http_status, 529 (int) hr->ec); 530 } 531 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 melt_reveal_run (void *cls, 557 const struct TALER_TESTING_Command *cmd, 558 struct TALER_TESTING_Interpreter *is) 559 { 560 struct RevealMeltState *rrs = cls; 561 struct MeltState *ms; 562 const struct TALER_TESTING_Command *melt_cmd; 563 564 rrs->cmd = cmd; 565 rrs->is = is; 566 melt_cmd = TALER_TESTING_interpreter_lookup_command (is, 567 rrs->melt_reference); 568 if (NULL == melt_cmd) 569 { 570 GNUNET_break (0); 571 TALER_TESTING_interpreter_fail (rrs->is); 572 return; 573 } 574 GNUNET_assert (melt_cmd->run == &melt_run); 575 ms = melt_cmd->cls; 576 ms->reveal_melt_input.rms = &ms->rms; 577 ms->reveal_melt_input.melt_input = &ms->melt_input; 578 ms->reveal_melt_input.blinding_seed = ms->no_blinding_seed 579 ? NULL 580 : &ms->blinding_seed; 581 ms->reveal_melt_input.num_blinding_values = ms->num_blinding_values; 582 ms->reveal_melt_input.blinding_values = ms->blinding_values; 583 ms->reveal_melt_input.noreveal_index = ms->noreveal_index; 584 rrs->rmh = TALER_EXCHANGE_post_reveal_melt_create ( 585 TALER_TESTING_interpreter_get_context (is), 586 TALER_TESTING_get_exchange_url (is), 587 &ms->reveal_melt_input); 588 if (NULL == rrs->rmh) 589 { 590 GNUNET_break (0); 591 TALER_TESTING_interpreter_fail (is); 592 return; 593 } 594 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 melt_reveal_cleanup (void *cls, 610 const struct TALER_TESTING_Command *cmd) 611 { 612 struct RevealMeltState *rrs = cls; 613 614 (void) cmd; 615 if (NULL != rrs->rmh) 616 { 617 TALER_TESTING_command_incomplete (rrs->is, 618 cmd->label); 619 TALER_EXCHANGE_post_reveal_melt_cancel (rrs->rmh); 620 rrs->rmh = NULL; 621 } 622 if (NULL != rrs->retry_task) 623 { 624 GNUNET_SCHEDULER_cancel (rrs->retry_task); 625 rrs->retry_task = NULL; 626 } 627 628 for (unsigned int j = 0; j < rrs->num_fresh_coins; j++) 629 { 630 TALER_denom_sig_free (&rrs->fresh_coins[j].sig); 631 TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof); 632 GNUNET_free (rrs->fresh_coins[j].age_commitment_proof); 633 } 634 GNUNET_free (rrs->fresh_coins); 635 GNUNET_free (rrs->psa); 636 rrs->num_fresh_coins = 0; 637 GNUNET_free (rrs); 638 } 639 640 641 /** 642 * Task scheduled to re-try #melt_run. 643 * 644 * @param cls a `struct RefreshMeltState` 645 */ 646 static void 647 do_melt_retry (void *cls) 648 { 649 struct MeltState *rms = cls; 650 651 rms->retry_task = NULL; 652 TALER_TESTING_touch_cmd (rms->is); 653 melt_run (rms, 654 NULL, 655 rms->is); 656 } 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 melt_cb (struct MeltState *ms, 669 const struct TALER_EXCHANGE_PostMeltResponse *mr) 670 { 671 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 672 673 ms->mh = NULL; 674 if (ms->expected_response_code != hr->http_status) 675 { 676 if (0 != ms->do_retry) 677 { 678 ms->do_retry--; 679 if ( (0 == hr->http_status) || 680 (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || 681 (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) 682 { 683 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 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) 689 ms->backoff = GNUNET_TIME_UNIT_ZERO; 690 else 691 ms->backoff = GNUNET_TIME_randomized_backoff (ms->backoff, 692 MAX_BACKOFF); 693 ms->total_backoff = GNUNET_TIME_relative_add (ms->total_backoff, 694 ms->backoff); 695 TALER_TESTING_inc_tries (ms->is); 696 ms->retry_task = GNUNET_SCHEDULER_add_delayed (ms->backoff, 697 &do_melt_retry, 698 ms); 699 return; 700 } 701 } 702 TALER_TESTING_unexpected_status_with_body (ms->is, 703 hr->http_status, 704 ms->expected_response_code, 705 hr->reply); 706 return; 707 } 708 if (MHD_HTTP_OK == hr->http_status) 709 { 710 ms->noreveal_index = mr->details.ok.noreveal_index; 711 if (mr->details.ok.num_melt_blinding_values != ms->num_fresh_coins) 712 { 713 GNUNET_break (0); 714 TALER_TESTING_interpreter_fail (ms->is); 715 return; 716 } 717 ms->no_blinding_seed = (NULL == mr->details.ok.blinding_seed); 718 if (NULL != mr->details.ok.blinding_seed) 719 ms->blinding_seed = *mr->details.ok.blinding_seed; 720 ms->num_blinding_values = mr->details.ok.num_melt_blinding_values; 721 if (NULL != ms->blinding_values) 722 { 723 for (unsigned int i = 0; i < ms->num_blinding_values; i++) 724 TALER_denom_ewv_free (&ms->blinding_values[i]); 725 GNUNET_free (ms->blinding_values); 726 } 727 ms->blinding_values = GNUNET_new_array ( 728 ms->num_blinding_values, 729 struct TALER_ExchangeBlindingValues); 730 for (unsigned int i = 0; i<ms->num_blinding_values; i++) 731 { 732 TALER_denom_ewv_copy (&ms->blinding_values[i], 733 &mr->details.ok.melt_blinding_values[i]); 734 } 735 } 736 if (0 != ms->total_backoff.rel_value_us) 737 { 738 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 739 "Total melt backoff for %s was %s\n", 740 ms->cmd->label, 741 GNUNET_STRINGS_relative_time_to_string (ms->total_backoff, 742 true)); 743 } 744 if (ms->double_melt) 745 { 746 TALER_LOG_DEBUG ("Doubling the melt (%s)\n", 747 ms->cmd->label); 748 ms->mh = TALER_EXCHANGE_post_melt_create ( 749 TALER_TESTING_interpreter_get_context (ms->is), 750 TALER_TESTING_get_exchange_url (ms->is), 751 TALER_TESTING_get_keys (ms->is), 752 &ms->rms, 753 &ms->melt_input); 754 GNUNET_assert (NULL != ms->mh); 755 GNUNET_assert (TALER_EC_NONE == 756 TALER_EXCHANGE_post_melt_start (ms->mh, 757 &melt_cb, 758 ms)); 759 ms->double_melt = false; 760 return; 761 } 762 TALER_TESTING_interpreter_next (ms->is); 763 } 764 765 766 /** 767 * Run the command. 768 * 769 * @param cls closure. 770 * @param cmd the command to execute. 771 * @param is the interpreter state. 772 */ 773 static void 774 melt_run (void *cls, 775 const struct TALER_TESTING_Command *cmd, 776 struct TALER_TESTING_Interpreter *is) 777 { 778 static const char *default_melt_fresh_amounts[] = { 779 "EUR:1", "EUR:1", "EUR:1", "EUR:0.1", 780 NULL 781 }; 782 struct MeltState *rms = cls; 783 unsigned int num_fresh_coins; 784 const char **melt_fresh_amounts; 785 786 rms->cmd = cmd; 787 if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts)) 788 melt_fresh_amounts = default_melt_fresh_amounts; 789 rms->is = is; 790 rms->noreveal_index = UINT16_MAX; 791 TALER_refresh_master_setup_random (&rms->rms); 792 for (num_fresh_coins = 0; 793 NULL != melt_fresh_amounts[num_fresh_coins]; 794 num_fresh_coins++) 795 ; 796 rms->num_fresh_coins = num_fresh_coins; 797 /* Free old data structure in case this is a retry! */ 798 if (NULL != rms->fresh_pks) 799 { 800 for (unsigned int i = 0; i < rms->num_fresh_coins; i++) 801 TALER_denom_pub_free (&rms->fresh_pks[i].key); 802 GNUNET_free (rms->fresh_pks); 803 } 804 rms->fresh_pks = GNUNET_new_array ( 805 num_fresh_coins, 806 struct TALER_EXCHANGE_DenomPublicKey); 807 { 808 struct TALER_Amount melt_amount; 809 struct TALER_Amount fresh_amount; 810 const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; 811 const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL; 812 const struct TALER_DenominationSignature *melt_sig; 813 const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; 814 const struct TALER_TESTING_Command *coin_command; 815 bool age_restricted_denom; 816 817 if (NULL == (coin_command 818 = TALER_TESTING_interpreter_lookup_command ( 819 is, 820 rms->coin_reference))) 821 { 822 GNUNET_break (0); 823 TALER_TESTING_interpreter_fail (rms->is); 824 return; 825 } 826 827 if (GNUNET_OK != 828 TALER_TESTING_get_trait_coin_priv (coin_command, 829 0, 830 &rms->melt_priv)) 831 { 832 GNUNET_break (0); 833 TALER_TESTING_interpreter_fail (rms->is); 834 return; 835 } 836 if (GNUNET_OK != 837 TALER_TESTING_get_trait_age_commitment_proof (coin_command, 838 0, 839 &age_commitment_proof)) 840 { 841 GNUNET_break (0); 842 TALER_TESTING_interpreter_fail (rms->is); 843 return; 844 } 845 846 if (GNUNET_OK != 847 TALER_TESTING_get_trait_h_age_commitment (coin_command, 848 0, 849 &h_age_commitment)) 850 { 851 GNUNET_break (0); 852 TALER_TESTING_interpreter_fail (rms->is); 853 return; 854 } 855 if (GNUNET_OK != 856 TALER_TESTING_get_trait_denom_sig (coin_command, 857 0, 858 &melt_sig)) 859 { 860 GNUNET_break (0); 861 TALER_TESTING_interpreter_fail (rms->is); 862 return; 863 } 864 if (GNUNET_OK != 865 TALER_TESTING_get_trait_denom_pub (coin_command, 866 0, 867 &melt_denom_pub)) 868 { 869 GNUNET_break (0); 870 TALER_TESTING_interpreter_fail (rms->is); 871 return; 872 } 873 874 /* Melt amount starts with the melt fee of the old coin; we'll add the 875 values and withdraw fees of the fresh coins next */ 876 melt_amount = melt_denom_pub->fees.refresh; 877 age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0; 878 GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof)); 879 GNUNET_assert ((NULL == age_commitment_proof) || 880 (0 < age_commitment_proof->commitment.num)); 881 for (unsigned int i = 0; i<num_fresh_coins; i++) 882 { 883 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; 884 885 if (GNUNET_OK != 886 TALER_string_to_amount (melt_fresh_amounts[i], 887 &fresh_amount)) 888 { 889 GNUNET_break (0); 890 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 891 "Failed to parse amount `%s' at index %u\n", 892 melt_fresh_amounts[i], 893 i); 894 TALER_TESTING_interpreter_fail (rms->is); 895 return; 896 } 897 fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is), 898 &fresh_amount, 899 age_restricted_denom); 900 if (NULL == fresh_pk) 901 { 902 GNUNET_break (0); 903 /* Subroutine logs specific error */ 904 TALER_TESTING_interpreter_fail (rms->is); 905 return; 906 } 907 GNUNET_assert (0 <= 908 TALER_amount_add (&melt_amount, 909 &melt_amount, 910 &fresh_amount)); 911 GNUNET_assert (0 <= 912 TALER_amount_add (&melt_amount, 913 &melt_amount, 914 &fresh_pk->fees.withdraw)); 915 rms->fresh_pks[i] = *fresh_pk; 916 /* Make a deep copy of the RSA key */ 917 TALER_denom_pub_copy (&rms->fresh_pks[i].key, 918 &fresh_pk->key); 919 } /* end for */ 920 921 rms->melt_input.melt_priv = *rms->melt_priv; 922 GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv, 923 &rms->melt_pub.eddsa_pub); 924 rms->melt_input.melt_amount = melt_amount; 925 rms->melt_input.melt_sig = *melt_sig; 926 rms->melt_input.melt_pk = *melt_denom_pub; 927 928 if (NULL != age_commitment_proof) 929 { 930 GNUNET_assert (NULL != h_age_commitment); 931 rms->melt_input.melt_age_commitment_proof = age_commitment_proof; 932 rms->melt_input.melt_h_age_commitment = h_age_commitment; 933 } 934 rms->melt_input.fresh_denom_pubs = rms->fresh_pks; 935 rms->melt_input.num_fresh_denom_pubs = num_fresh_coins; 936 937 GNUNET_assert (age_restricted_denom == 938 (NULL != age_commitment_proof)); 939 GNUNET_assert ((NULL == age_commitment_proof) || 940 (0 < age_commitment_proof->commitment.num)); 941 942 rms->che.type = TALER_EXCHANGE_CTT_MELT; 943 rms->che.amount = melt_amount; 944 if (NULL != age_commitment_proof) 945 rms->che.details.melt.h_age_commitment = *h_age_commitment; 946 else 947 rms->che.details.melt.no_hac = true; 948 949 rms->mh = TALER_EXCHANGE_post_melt_create ( 950 TALER_TESTING_interpreter_get_context (is), 951 TALER_TESTING_get_exchange_url (is), 952 TALER_TESTING_get_keys (is), 953 &rms->rms, 954 &rms->melt_input); 955 956 if (NULL == rms->mh) 957 { 958 GNUNET_break (0); 959 TALER_TESTING_interpreter_fail (rms->is); 960 return; 961 } 962 GNUNET_assert (TALER_EC_NONE == 963 TALER_EXCHANGE_post_melt_start (rms->mh, 964 &melt_cb, 965 rms)); 966 } 967 } 968 969 970 /** 971 * Free the "refresh melt" CMD state, and possibly cancel a 972 * pending operation thereof. 973 * 974 * @param cls closure, must be a `struct RefreshMeltState`. 975 * @param cmd the command which is being cleaned up. 976 */ 977 static void 978 melt_cleanup (void *cls, 979 const struct TALER_TESTING_Command *cmd) 980 { 981 struct MeltState *rms = cls; 982 983 (void) cmd; 984 if (NULL != rms->mh) 985 { 986 TALER_TESTING_command_incomplete (rms->is, 987 cmd->label); 988 TALER_EXCHANGE_post_melt_cancel (rms->mh); 989 rms->mh = NULL; 990 } 991 if (NULL != rms->retry_task) 992 { 993 GNUNET_SCHEDULER_cancel (rms->retry_task); 994 rms->retry_task = NULL; 995 } 996 if (NULL != rms->fresh_pks) 997 { 998 for (unsigned int i = 0; i < rms->num_fresh_coins; i++) 999 TALER_denom_pub_free (&rms->fresh_pks[i].key); 1000 GNUNET_free (rms->fresh_pks); 1001 } 1002 if (NULL != rms->blinding_values) 1003 { 1004 for (unsigned int i = 0; i < rms->num_blinding_values; i++) 1005 TALER_denom_ewv_free (&rms->blinding_values[i]); 1006 GNUNET_free (rms->blinding_values); 1007 } 1008 GNUNET_free (rms->melt_fresh_amounts); 1009 GNUNET_free (rms); 1010 } 1011 1012 1013 /** 1014 * Offer internal data to the "refresh melt" CMD. 1015 * 1016 * @param cls closure. 1017 * @param[out] ret result (could be anything). 1018 * @param trait name of the trait. 1019 * @param index index number of the object to offer. 1020 * @return #GNUNET_OK on success. 1021 */ 1022 static enum GNUNET_GenericReturnValue 1023 melt_traits (void *cls, 1024 const void **ret, 1025 const char *trait, 1026 unsigned int index) 1027 { 1028 struct MeltState *rms = cls; 1029 1030 if (index >= rms->num_fresh_coins) 1031 { 1032 GNUNET_break (0); 1033 return GNUNET_SYSERR; 1034 } 1035 { 1036 struct TALER_TESTING_Trait traits[] = { 1037 TALER_TESTING_make_trait_denom_pub (index, 1038 &rms->fresh_pks[index]), 1039 TALER_TESTING_make_trait_coin_priv (0, 1040 rms->melt_priv), 1041 TALER_TESTING_make_trait_coin_pub (0, 1042 &rms->melt_pub), 1043 TALER_TESTING_make_trait_coin_history (0, 1044 &rms->che), 1045 TALER_TESTING_make_trait_age_commitment_proof ( 1046 index, 1047 rms->melt_input.melt_age_commitment_proof), 1048 TALER_TESTING_make_trait_h_age_commitment ( 1049 index, 1050 rms->melt_input.melt_h_age_commitment), 1051 TALER_TESTING_make_trait_refresh_seed (&rms->rms), 1052 (NULL != rms->reveal_melt_input.blinding_values) 1053 ? TALER_TESTING_make_trait_exchange_blinding_values ( 1054 index, 1055 &rms->reveal_melt_input.blinding_values[index]) 1056 : TALER_TESTING_trait_end (), 1057 TALER_TESTING_trait_end () 1058 }; 1059 1060 return TALER_TESTING_get_trait (traits, 1061 ret, 1062 trait, 1063 index); 1064 } 1065 } 1066 1067 1068 /** 1069 * Parse list of amounts for melt operation. 1070 * 1071 * @param[in,out] rms where to store the list 1072 * @param ap NULL-termianted list of amounts to be melted (one per fresh coin) 1073 * @return #GNUNET_OK on success 1074 */ 1075 static enum GNUNET_GenericReturnValue 1076 parse_amounts (struct MeltState *rms, 1077 va_list ap) 1078 { 1079 unsigned int len; 1080 unsigned int off; 1081 const char *amount; 1082 1083 len = 0; 1084 off = 0; 1085 while (NULL != (amount = va_arg (ap, const char *))) 1086 { 1087 if (len == off) 1088 { 1089 struct TALER_Amount a; 1090 1091 GNUNET_array_grow (rms->melt_fresh_amounts, 1092 len, 1093 off + 16); 1094 if (GNUNET_OK != 1095 TALER_string_to_amount (amount, &a)) 1096 { 1097 GNUNET_break (0); 1098 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1099 "Failed to parse amount `%s' at index %u\n", 1100 amount, off); 1101 GNUNET_free (rms->melt_fresh_amounts); 1102 rms->melt_fresh_amounts = NULL; 1103 return GNUNET_SYSERR; 1104 } 1105 rms->melt_fresh_amounts[off++] = amount; 1106 } 1107 } 1108 if (0 == off) 1109 return GNUNET_OK; /* no amounts given == use defaults! */ 1110 /* ensure NULL-termination */ 1111 GNUNET_array_grow (rms->melt_fresh_amounts, 1112 len, 1113 off + 1); 1114 return GNUNET_OK; 1115 } 1116 1117 1118 struct TALER_TESTING_Command 1119 TALER_TESTING_cmd_melt (const char *label, 1120 const char *coin_reference, 1121 unsigned int expected_response_code, 1122 ...) 1123 { 1124 struct MeltState *rms; 1125 va_list ap; 1126 1127 rms = GNUNET_new (struct MeltState); 1128 rms->coin_reference = coin_reference; 1129 rms->expected_response_code = expected_response_code; 1130 va_start (ap, 1131 expected_response_code); 1132 GNUNET_assert (GNUNET_OK == 1133 parse_amounts (rms, ap)); 1134 va_end (ap); 1135 { 1136 struct TALER_TESTING_Command cmd = { 1137 .label = label, 1138 .cls = rms, 1139 .run = &melt_run, 1140 .cleanup = &melt_cleanup, 1141 .traits = &melt_traits 1142 }; 1143 1144 return cmd; 1145 } 1146 } 1147 1148 1149 struct TALER_TESTING_Command 1150 TALER_TESTING_cmd_melt_double (const char *label, 1151 const char *coin_reference, 1152 unsigned int expected_response_code, 1153 ...) 1154 { 1155 struct MeltState *rms; 1156 va_list ap; 1157 1158 rms = GNUNET_new (struct MeltState); 1159 rms->coin_reference = coin_reference; 1160 rms->expected_response_code = expected_response_code; 1161 rms->double_melt = true; 1162 va_start (ap, 1163 expected_response_code); 1164 GNUNET_assert (GNUNET_OK == 1165 parse_amounts (rms, ap)); 1166 va_end (ap); 1167 { 1168 struct TALER_TESTING_Command cmd = { 1169 .label = label, 1170 .cls = rms, 1171 .run = &melt_run, 1172 .cleanup = &melt_cleanup, 1173 .traits = &melt_traits 1174 }; 1175 1176 return cmd; 1177 } 1178 } 1179 1180 1181 struct TALER_TESTING_Command 1182 TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd) 1183 { 1184 struct MeltState *rms; 1185 1186 GNUNET_assert (&melt_run == cmd.run); 1187 rms = cmd.cls; 1188 rms->do_retry = NUM_RETRIES; 1189 return cmd; 1190 } 1191 1192 1193 /** 1194 * Offer internal data from a "refresh reveal" CMD. 1195 * 1196 * @param cls closure. 1197 * @param[out] ret result (could be anything). 1198 * @param trait name of the trait. 1199 * @param index index number of the object to offer. 1200 * @return #GNUNET_OK on success. 1201 */ 1202 static enum GNUNET_GenericReturnValue 1203 melt_reveal_traits (void *cls, 1204 const void **ret, 1205 const char *trait, 1206 unsigned int index) 1207 { 1208 struct RevealMeltState *rrs = cls; 1209 1210 if (index >= rrs->num_fresh_coins) 1211 return GNUNET_SYSERR; 1212 1213 { 1214 struct TALER_TESTING_Trait traits[] = { 1215 TALER_TESTING_make_trait_coin_priv ( 1216 index, 1217 &rrs->fresh_coins[index].coin_priv), 1218 TALER_TESTING_make_trait_coin_pub ( 1219 index, 1220 &rrs->fresh_coins[index].coin_pub), 1221 TALER_TESTING_make_trait_age_commitment_proof ( 1222 index, 1223 rrs->fresh_coins[index].age_commitment_proof), 1224 TALER_TESTING_make_trait_h_age_commitment ( 1225 index, 1226 &rrs->fresh_coins[index].h_age_commitment), 1227 TALER_TESTING_make_trait_denom_pub ( 1228 index, 1229 rrs->fresh_coins[index].pk), 1230 TALER_TESTING_make_trait_denom_sig ( 1231 index, 1232 &rrs->fresh_coins[index].sig), 1233 TALER_TESTING_make_trait_blinding_key ( 1234 index, 1235 &rrs->fresh_coins[index].blinding_key), 1236 TALER_TESTING_make_trait_array_length ( 1237 &rrs->num_fresh_coins), 1238 TALER_TESTING_make_trait_fresh_coins ( 1239 (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins), 1240 TALER_TESTING_make_trait_planchet_secrets (index, 1241 &rrs->psa[index]), 1242 TALER_TESTING_trait_end () 1243 }; 1244 1245 return TALER_TESTING_get_trait (traits, 1246 ret, 1247 trait, 1248 index); 1249 } 1250 } 1251 1252 1253 struct TALER_TESTING_Command 1254 TALER_TESTING_cmd_melt_reveal (const char *label, 1255 const char *melt_reference, 1256 unsigned int expected_response_code) 1257 { 1258 struct RevealMeltState *rrs; 1259 1260 rrs = GNUNET_new (struct RevealMeltState); 1261 rrs->melt_reference = melt_reference; 1262 rrs->expected_response_code = expected_response_code; 1263 { 1264 struct TALER_TESTING_Command cmd = { 1265 .cls = rrs, 1266 .label = label, 1267 .run = &melt_reveal_run, 1268 .cleanup = &melt_reveal_cleanup, 1269 .traits = &melt_reveal_traits 1270 }; 1271 1272 return cmd; 1273 } 1274 } 1275 1276 1277 struct TALER_TESTING_Command 1278 TALER_TESTING_cmd_melt_reveal_with_retry (struct TALER_TESTING_Command cmd) 1279 { 1280 struct RevealMeltState *rrs; 1281 1282 GNUNET_assert (&melt_reveal_run == cmd.run); 1283 rrs = cmd.cls; 1284 rrs->do_retry = NUM_RETRIES; 1285 return cmd; 1286 }