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