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