testing_api_cmd_refresh.c (35300B)
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 * Fresh age commitment for the coin with proof and its hash, NULL if not 76 * applicable. 77 */ 78 struct TALER_AgeCommitmentProof *age_commitment_proof; 79 struct TALER_AgeCommitmentHashP h_age_commitment; 80 81 /** 82 * The blinding key (needed for recoup operations). 83 */ 84 union GNUNET_CRYPTO_BlindingSecretP blinding_key; 85 86 }; 87 88 89 /** 90 * State for a "refresh melt" command. 91 */ 92 struct MeltState 93 { 94 95 /** 96 * Reference to reserve_withdraw operations for coin to 97 * be used for the /refresh/melt operation. 98 */ 99 const char *coin_reference; 100 101 /** 102 * Our command. 103 */ 104 const struct TALER_TESTING_Command *cmd; 105 106 /** 107 * Reference to a previous melt command. 108 */ 109 const char *melt_reference; 110 111 /** 112 * Melt handle while operation is running. 113 */ 114 struct TALER_EXCHANGE_MeltHandle_v27 *mh; 115 116 /** 117 * Expected entry in the coin history created by this 118 * operation. 119 */ 120 struct TALER_EXCHANGE_CoinHistoryEntry che; 121 122 /** 123 * Interpreter state. 124 */ 125 struct TALER_TESTING_Interpreter *is; 126 127 /** 128 * The input for the call to /melt 129 */ 130 struct TALER_EXCHANGE_MeltInput melt_input; 131 132 /** 133 * Length of the @a blinding_values array with the exchange values 134 * and blinding keys we are using. 135 */ 136 unsigned int num_blinding_values; 137 138 /** 139 * Blinding values returned per coin. 140 */ 141 struct TALER_ExchangeBlindingValues *blinding_values; 142 143 /** 144 * The input for the call to /reveal-melt 145 */ 146 struct TALER_EXCHANGE_RevealMeltInput reveal_melt_input; 147 148 /** 149 * Array of the denomination public keys 150 * corresponding to the @e num_fresh_coins; 151 */ 152 struct TALER_EXCHANGE_DenomPublicKey *fresh_pks; 153 154 /** 155 * Private key of the dirty coin being melted. 156 */ 157 const struct TALER_CoinSpendPrivateKeyP *melt_priv; 158 159 /** 160 * Public key of the dirty coin being melted. 161 */ 162 struct TALER_CoinSpendPublicKeyP melt_pub; 163 164 /** 165 * Entropy seed for the refresh-melt operation. 166 */ 167 struct TALER_RefreshMasterSecretP rms; 168 169 /** 170 * The master seed to derive the nonces from 171 */ 172 struct TALER_PublicRefreshMasterSeedP refresh_seed; 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_RevealMeltHandle *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_RevealMeltResponse *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 (rrs->is, 460 hr->http_status, 461 rrs->expected_response_code); 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 499 if (NULL != coin->age_commitment_proof) 500 { 501 fc->age_commitment_proof = 502 TALER_age_commitment_proof_duplicate (coin->age_commitment_proof); 503 fc->h_age_commitment = coin->h_age_commitment; 504 } 505 506 TALER_denom_sig_copy (&fc->sig, 507 &coin->sig); 508 } 509 if (0 != rrs->total_backoff.rel_value_us) 510 { 511 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 512 "Total reveal backoff for %s was %s\n", 513 rrs->cmd->label, 514 GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff, 515 true)); 516 } 517 break; 518 default: 519 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 520 "Unknown HTTP status %u/%d\n", 521 hr->http_status, 522 (int) hr->ec); 523 } 524 TALER_TESTING_interpreter_next (rrs->is); 525 } 526 527 528 /** 529 * Run the command. 530 * 531 * @param cls closure. 532 * @param cmd the command to execute. 533 * @param is the interpreter state. 534 */ 535 static void 536 melt_run (void *cls, 537 const struct TALER_TESTING_Command *cmd, 538 struct TALER_TESTING_Interpreter *is); 539 540 541 /** 542 * Run the command. 543 * 544 * @param cls closure. 545 * @param cmd the command to execute. 546 * @param is the interpreter state. 547 */ 548 static void 549 melt_reveal_run (void *cls, 550 const struct TALER_TESTING_Command *cmd, 551 struct TALER_TESTING_Interpreter *is) 552 { 553 struct RevealMeltState *rrs = cls; 554 struct MeltState *rms; 555 const struct TALER_TESTING_Command *melt_cmd; 556 557 rrs->cmd = cmd; 558 rrs->is = is; 559 melt_cmd = TALER_TESTING_interpreter_lookup_command (is, 560 rrs->melt_reference); 561 if (NULL == melt_cmd) 562 { 563 GNUNET_break (0); 564 TALER_TESTING_interpreter_fail (rrs->is); 565 return; 566 } 567 GNUNET_assert (melt_cmd->run == &melt_run); 568 rms = melt_cmd->cls; 569 rms->reveal_melt_input.rms = &rms->rms; 570 rms->reveal_melt_input.melt_input = &rms->melt_input; 571 rms->reveal_melt_input.blinding_seed = rms->no_blinding_seed 572 ? NULL 573 : &rms->blinding_seed; 574 rms->reveal_melt_input.num_blinding_values = rms->num_blinding_values; 575 rms->reveal_melt_input.blinding_values = rms->blinding_values; 576 rms->reveal_melt_input.noreveal_index = rms->noreveal_index; 577 rrs->rmh = TALER_EXCHANGE_reveal_melt ( 578 TALER_TESTING_interpreter_get_context (is), 579 TALER_TESTING_get_exchange_url (is), 580 &rms->reveal_melt_input, 581 &reveal_cb, 582 rrs); 583 if (NULL == rrs->rmh) 584 { 585 GNUNET_break (0); 586 TALER_TESTING_interpreter_fail (is); 587 return; 588 } 589 } 590 591 592 /** 593 * Free the state from a "refresh reveal" CMD, and possibly 594 * cancel a pending operation thereof. 595 * 596 * @param cls closure. 597 * @param cmd the command which is being cleaned up. 598 */ 599 static void 600 melt_reveal_cleanup (void *cls, 601 const struct TALER_TESTING_Command *cmd) 602 { 603 struct RevealMeltState *rrs = cls; 604 605 (void) cmd; 606 if (NULL != rrs->rmh) 607 { 608 TALER_TESTING_command_incomplete (rrs->is, 609 cmd->label); 610 TALER_EXCHANGE_reveal_melt_cancel (rrs->rmh); 611 rrs->rmh = NULL; 612 } 613 if (NULL != rrs->retry_task) 614 { 615 GNUNET_SCHEDULER_cancel (rrs->retry_task); 616 rrs->retry_task = NULL; 617 } 618 619 for (unsigned int j = 0; j < rrs->num_fresh_coins; j++) 620 { 621 TALER_denom_sig_free (&rrs->fresh_coins[j].sig); 622 TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof); 623 GNUNET_free (rrs->fresh_coins[j].age_commitment_proof); 624 } 625 GNUNET_free (rrs->fresh_coins); 626 GNUNET_free (rrs->psa); 627 rrs->num_fresh_coins = 0; 628 GNUNET_free (rrs); 629 } 630 631 632 /** 633 * Task scheduled to re-try #melt_run. 634 * 635 * @param cls a `struct RefreshMeltState` 636 */ 637 static void 638 do_melt_retry (void *cls) 639 { 640 struct MeltState *rms = cls; 641 642 rms->retry_task = NULL; 643 TALER_TESTING_touch_cmd (rms->is); 644 melt_run (rms, 645 NULL, 646 rms->is); 647 } 648 649 650 /** 651 * Callback for a " /melt" operation; checks if the HTTP 652 * response code is okay and re-run the melt operation if the 653 * CMD was set to do so. 654 * 655 * @param cls closure. 656 * @param mr melt response details 657 */ 658 static void 659 melt_cb (void *cls, 660 const struct TALER_EXCHANGE_MeltResponse_v27 *mr) 661 { 662 struct MeltState *rms = cls; 663 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 664 665 rms->mh = NULL; 666 if (rms->expected_response_code != hr->http_status) 667 { 668 if (0 != rms->do_retry) 669 { 670 rms->do_retry--; 671 if ( (0 == hr->http_status) || 672 (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) || 673 (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) ) 674 { 675 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 676 "Retrying refresh melt failed with %u/%d\n", 677 hr->http_status, 678 (int) hr->ec); 679 /* on DB conflicts, do not use backoff */ 680 if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) 681 rms->backoff = GNUNET_TIME_UNIT_ZERO; 682 else 683 rms->backoff = GNUNET_TIME_randomized_backoff (rms->backoff, 684 MAX_BACKOFF); 685 rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff, 686 rms->backoff); 687 TALER_TESTING_inc_tries (rms->is); 688 rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff, 689 &do_melt_retry, 690 rms); 691 return; 692 } 693 } 694 TALER_TESTING_unexpected_status_with_body (rms->is, 695 hr->http_status, 696 rms->expected_response_code, 697 hr->reply); 698 return; 699 } 700 if (MHD_HTTP_OK == hr->http_status) 701 { 702 rms->noreveal_index = mr->details.ok.noreveal_index; 703 if (mr->details.ok.num_melt_blinding_values != rms->num_fresh_coins) 704 { 705 GNUNET_break (0); 706 TALER_TESTING_interpreter_fail (rms->is); 707 return; 708 } 709 rms->no_blinding_seed = (NULL == mr->details.ok.blinding_seed); 710 if (NULL != mr->details.ok.blinding_seed) 711 rms->blinding_seed = *mr->details.ok.blinding_seed; 712 rms->num_blinding_values = mr->details.ok.num_melt_blinding_values; 713 if (NULL != rms->blinding_values) 714 { 715 GNUNET_break (0); /* can this this happen? Check! */ 716 for (unsigned int i = 0; i < rms->num_blinding_values; i++) 717 TALER_denom_ewv_free (&rms->blinding_values[i]); 718 GNUNET_free (rms->blinding_values); 719 } 720 rms->blinding_values = GNUNET_new_array ( 721 rms->num_blinding_values, 722 struct TALER_ExchangeBlindingValues); 723 for (unsigned int i = 0; i<rms->num_blinding_values; i++) 724 { 725 TALER_denom_ewv_copy (&rms->blinding_values[i], 726 &mr->details.ok.melt_blinding_values[i]); 727 } 728 } 729 if (0 != rms->total_backoff.rel_value_us) 730 { 731 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 732 "Total melt backoff for %s was %s\n", 733 rms->cmd->label, 734 GNUNET_STRINGS_relative_time_to_string (rms->total_backoff, 735 true)); 736 } 737 if (rms->double_melt) 738 { 739 TALER_LOG_DEBUG ("Doubling the melt (%s)\n", 740 rms->cmd->label); 741 rms->mh = TALER_EXCHANGE_melt_v27 ( 742 TALER_TESTING_interpreter_get_context (rms->is), 743 TALER_TESTING_get_exchange_url (rms->is), 744 TALER_TESTING_get_keys (rms->is), 745 &rms->rms, 746 &rms->melt_input, 747 &melt_cb, 748 rms); 749 rms->double_melt = false; 750 return; 751 } 752 TALER_TESTING_interpreter_next (rms->is); 753 } 754 755 756 /** 757 * Run the command. 758 * 759 * @param cls closure. 760 * @param cmd the command to execute. 761 * @param is the interpreter state. 762 */ 763 static void 764 melt_run (void *cls, 765 const struct TALER_TESTING_Command *cmd, 766 struct TALER_TESTING_Interpreter *is) 767 { 768 static const char *default_melt_fresh_amounts[] = { 769 "EUR:1", "EUR:1", "EUR:1", "EUR:0.1", 770 NULL 771 }; 772 struct MeltState *rms = cls; 773 unsigned int num_fresh_coins; 774 const char **melt_fresh_amounts; 775 776 rms->cmd = cmd; 777 if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts)) 778 melt_fresh_amounts = default_melt_fresh_amounts; 779 rms->is = is; 780 rms->noreveal_index = UINT16_MAX; 781 TALER_refresh_master_setup_random (&rms->rms); 782 for (num_fresh_coins = 0; 783 NULL != melt_fresh_amounts[num_fresh_coins]; 784 num_fresh_coins++) 785 ; 786 rms->num_fresh_coins = num_fresh_coins; 787 /* Free old data structure in case this is a retry! */ 788 if (NULL != rms->fresh_pks) 789 { 790 for (unsigned int i = 0; i < rms->num_fresh_coins; i++) 791 TALER_denom_pub_free (&rms->fresh_pks[i].key); 792 GNUNET_free (rms->fresh_pks); 793 } 794 rms->fresh_pks = GNUNET_new_array ( 795 num_fresh_coins, 796 struct TALER_EXCHANGE_DenomPublicKey); 797 { 798 struct TALER_Amount melt_amount; 799 struct TALER_Amount fresh_amount; 800 const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL; 801 const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL; 802 const struct TALER_DenominationSignature *melt_sig; 803 const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub; 804 const struct TALER_TESTING_Command *coin_command; 805 bool age_restricted_denom; 806 807 if (NULL == (coin_command 808 = TALER_TESTING_interpreter_lookup_command ( 809 is, 810 rms->coin_reference))) 811 { 812 GNUNET_break (0); 813 TALER_TESTING_interpreter_fail (rms->is); 814 return; 815 } 816 817 if (GNUNET_OK != 818 TALER_TESTING_get_trait_coin_priv (coin_command, 819 0, 820 &rms->melt_priv)) 821 { 822 GNUNET_break (0); 823 TALER_TESTING_interpreter_fail (rms->is); 824 return; 825 } 826 if (GNUNET_OK != 827 TALER_TESTING_get_trait_age_commitment_proof (coin_command, 828 0, 829 &age_commitment_proof)) 830 { 831 GNUNET_break (0); 832 TALER_TESTING_interpreter_fail (rms->is); 833 return; 834 } 835 836 if (GNUNET_OK != 837 TALER_TESTING_get_trait_h_age_commitment (coin_command, 838 0, 839 &h_age_commitment)) 840 { 841 GNUNET_break (0); 842 TALER_TESTING_interpreter_fail (rms->is); 843 return; 844 } 845 if (GNUNET_OK != 846 TALER_TESTING_get_trait_denom_sig (coin_command, 847 0, 848 &melt_sig)) 849 { 850 GNUNET_break (0); 851 TALER_TESTING_interpreter_fail (rms->is); 852 return; 853 } 854 if (GNUNET_OK != 855 TALER_TESTING_get_trait_denom_pub (coin_command, 856 0, 857 &melt_denom_pub)) 858 { 859 GNUNET_break (0); 860 TALER_TESTING_interpreter_fail (rms->is); 861 return; 862 } 863 864 /* Melt amount starts with the melt fee of the old coin; we'll add the 865 values and withdraw fees of the fresh coins next */ 866 melt_amount = melt_denom_pub->fees.refresh; 867 age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0; 868 GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof)); 869 GNUNET_assert ((NULL == age_commitment_proof) || 870 (0 < age_commitment_proof->commitment.num)); 871 for (unsigned int i = 0; i<num_fresh_coins; i++) 872 { 873 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk; 874 875 if (GNUNET_OK != 876 TALER_string_to_amount (melt_fresh_amounts[i], 877 &fresh_amount)) 878 { 879 GNUNET_break (0); 880 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 881 "Failed to parse amount `%s' at index %u\n", 882 melt_fresh_amounts[i], 883 i); 884 TALER_TESTING_interpreter_fail (rms->is); 885 return; 886 } 887 fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is), 888 &fresh_amount, 889 age_restricted_denom); 890 if (NULL == fresh_pk) 891 { 892 GNUNET_break (0); 893 /* Subroutine logs specific error */ 894 TALER_TESTING_interpreter_fail (rms->is); 895 return; 896 } 897 GNUNET_assert (0 <= 898 TALER_amount_add (&melt_amount, 899 &melt_amount, 900 &fresh_amount)); 901 GNUNET_assert (0 <= 902 TALER_amount_add (&melt_amount, 903 &melt_amount, 904 &fresh_pk->fees.withdraw)); 905 rms->fresh_pks[i] = *fresh_pk; 906 /* Make a deep copy of the RSA key */ 907 TALER_denom_pub_copy (&rms->fresh_pks[i].key, 908 &fresh_pk->key); 909 } /* end for */ 910 911 rms->melt_input.melt_priv = *rms->melt_priv; 912 GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv, 913 &rms->melt_pub.eddsa_pub); 914 rms->melt_input.melt_amount = melt_amount; 915 rms->melt_input.melt_sig = *melt_sig; 916 rms->melt_input.melt_pk = *melt_denom_pub; 917 918 if (NULL != age_commitment_proof) 919 { 920 GNUNET_assert (NULL != h_age_commitment); 921 rms->melt_input.melt_age_commitment_proof = age_commitment_proof; 922 rms->melt_input.melt_h_age_commitment = h_age_commitment; 923 } 924 rms->melt_input.fresh_denom_pubs = rms->fresh_pks; 925 rms->melt_input.num_fresh_denom_pubs = num_fresh_coins; 926 927 GNUNET_assert (age_restricted_denom == 928 (NULL != age_commitment_proof)); 929 GNUNET_assert ((NULL == age_commitment_proof) || 930 (0 < age_commitment_proof->commitment.num)); 931 932 rms->che.type = TALER_EXCHANGE_CTT_MELT; 933 rms->che.amount = melt_amount; 934 if (NULL != age_commitment_proof) 935 rms->che.details.melt.h_age_commitment = *h_age_commitment; 936 else 937 rms->che.details.melt.no_hac = true; 938 939 rms->mh = TALER_EXCHANGE_melt_v27 ( 940 TALER_TESTING_interpreter_get_context (is), 941 TALER_TESTING_get_exchange_url (is), 942 TALER_TESTING_get_keys (is), 943 &rms->rms, 944 &rms->melt_input, 945 &melt_cb, 946 rms); 947 948 if (NULL == rms->mh) 949 { 950 GNUNET_break (0); 951 TALER_TESTING_interpreter_fail (rms->is); 952 return; 953 } 954 } 955 } 956 957 958 /** 959 * Free the "refresh melt" CMD state, and possibly cancel a 960 * pending operation thereof. 961 * 962 * @param cls closure, must be a `struct RefreshMeltState`. 963 * @param cmd the command which is being cleaned up. 964 */ 965 static void 966 melt_cleanup (void *cls, 967 const struct TALER_TESTING_Command *cmd) 968 { 969 struct MeltState *rms = cls; 970 971 (void) cmd; 972 if (NULL != rms->mh) 973 { 974 TALER_TESTING_command_incomplete (rms->is, 975 cmd->label); 976 TALER_EXCHANGE_melt_v27_cancel (rms->mh); 977 rms->mh = NULL; 978 } 979 if (NULL != rms->retry_task) 980 { 981 GNUNET_SCHEDULER_cancel (rms->retry_task); 982 rms->retry_task = NULL; 983 } 984 if (NULL != rms->fresh_pks) 985 { 986 for (unsigned int i = 0; i < rms->num_fresh_coins; i++) 987 TALER_denom_pub_free (&rms->fresh_pks[i].key); 988 GNUNET_free (rms->fresh_pks); 989 } 990 if (NULL != rms->blinding_values) 991 { 992 for (unsigned int i = 0; i < rms->num_blinding_values; i++) 993 TALER_denom_ewv_free (&rms->blinding_values[i]); 994 GNUNET_free (rms->blinding_values); 995 } 996 GNUNET_free (rms->melt_fresh_amounts); 997 GNUNET_free (rms); 998 } 999 1000 1001 /** 1002 * Offer internal data to the "refresh melt" CMD. 1003 * 1004 * @param cls closure. 1005 * @param[out] ret result (could be anything). 1006 * @param trait name of the trait. 1007 * @param index index number of the object to offer. 1008 * @return #GNUNET_OK on success. 1009 */ 1010 static enum GNUNET_GenericReturnValue 1011 melt_traits (void *cls, 1012 const void **ret, 1013 const char *trait, 1014 unsigned int index) 1015 { 1016 struct MeltState *rms = cls; 1017 1018 if (index >= rms->num_fresh_coins) 1019 { 1020 GNUNET_break (0); 1021 return GNUNET_SYSERR; 1022 } 1023 { 1024 struct TALER_TESTING_Trait traits[] = { 1025 TALER_TESTING_make_trait_denom_pub (index, 1026 &rms->fresh_pks[index]), 1027 TALER_TESTING_make_trait_coin_priv (0, 1028 rms->melt_priv), 1029 TALER_TESTING_make_trait_coin_pub (0, 1030 &rms->melt_pub), 1031 TALER_TESTING_make_trait_coin_history (0, 1032 &rms->che), 1033 TALER_TESTING_make_trait_age_commitment_proof ( 1034 index, 1035 rms->melt_input.melt_age_commitment_proof), 1036 TALER_TESTING_make_trait_h_age_commitment ( 1037 index, 1038 rms->melt_input.melt_h_age_commitment), 1039 TALER_TESTING_make_trait_refresh_secret (&rms->rms), 1040 (NULL != rms->reveal_melt_input.blinding_values) 1041 ? TALER_TESTING_make_trait_exchange_blinding_values ( 1042 index, 1043 &rms->reveal_melt_input.blinding_values[index]) 1044 : TALER_TESTING_trait_end (), 1045 TALER_TESTING_trait_end () 1046 }; 1047 1048 return TALER_TESTING_get_trait (traits, 1049 ret, 1050 trait, 1051 index); 1052 } 1053 } 1054 1055 1056 /** 1057 * Parse list of amounts for melt operation. 1058 * 1059 * @param[in,out] rms where to store the list 1060 * @param ap NULL-termianted list of amounts to be melted (one per fresh coin) 1061 * @return #GNUNET_OK on success 1062 */ 1063 static enum GNUNET_GenericReturnValue 1064 parse_amounts (struct MeltState *rms, 1065 va_list ap) 1066 { 1067 unsigned int len; 1068 unsigned int off; 1069 const char *amount; 1070 1071 len = 0; 1072 off = 0; 1073 while (NULL != (amount = va_arg (ap, const char *))) 1074 { 1075 if (len == off) 1076 { 1077 struct TALER_Amount a; 1078 1079 GNUNET_array_grow (rms->melt_fresh_amounts, 1080 len, 1081 off + 16); 1082 if (GNUNET_OK != 1083 TALER_string_to_amount (amount, &a)) 1084 { 1085 GNUNET_break (0); 1086 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1087 "Failed to parse amount `%s' at index %u\n", 1088 amount, off); 1089 GNUNET_free (rms->melt_fresh_amounts); 1090 rms->melt_fresh_amounts = NULL; 1091 return GNUNET_SYSERR; 1092 } 1093 rms->melt_fresh_amounts[off++] = amount; 1094 } 1095 } 1096 if (0 == off) 1097 return GNUNET_OK; /* no amounts given == use defaults! */ 1098 /* ensure NULL-termination */ 1099 GNUNET_array_grow (rms->melt_fresh_amounts, 1100 len, 1101 off + 1); 1102 return GNUNET_OK; 1103 } 1104 1105 1106 struct TALER_TESTING_Command 1107 TALER_TESTING_cmd_melt (const char *label, 1108 const char *coin_reference, 1109 unsigned int expected_response_code, 1110 ...) 1111 { 1112 struct MeltState *rms; 1113 va_list ap; 1114 1115 rms = GNUNET_new (struct MeltState); 1116 rms->coin_reference = coin_reference; 1117 rms->expected_response_code = expected_response_code; 1118 va_start (ap, 1119 expected_response_code); 1120 GNUNET_assert (GNUNET_OK == 1121 parse_amounts (rms, ap)); 1122 va_end (ap); 1123 { 1124 struct TALER_TESTING_Command cmd = { 1125 .label = label, 1126 .cls = rms, 1127 .run = &melt_run, 1128 .cleanup = &melt_cleanup, 1129 .traits = &melt_traits 1130 }; 1131 1132 return cmd; 1133 } 1134 } 1135 1136 1137 struct TALER_TESTING_Command 1138 TALER_TESTING_cmd_melt_double (const char *label, 1139 const char *coin_reference, 1140 unsigned int expected_response_code, 1141 ...) 1142 { 1143 struct MeltState *rms; 1144 va_list ap; 1145 1146 rms = GNUNET_new (struct MeltState); 1147 rms->coin_reference = coin_reference; 1148 rms->expected_response_code = expected_response_code; 1149 rms->double_melt = true; 1150 va_start (ap, 1151 expected_response_code); 1152 GNUNET_assert (GNUNET_OK == 1153 parse_amounts (rms, ap)); 1154 va_end (ap); 1155 { 1156 struct TALER_TESTING_Command cmd = { 1157 .label = label, 1158 .cls = rms, 1159 .run = &melt_run, 1160 .cleanup = &melt_cleanup, 1161 .traits = &melt_traits 1162 }; 1163 1164 return cmd; 1165 } 1166 } 1167 1168 1169 struct TALER_TESTING_Command 1170 TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd) 1171 { 1172 struct MeltState *rms; 1173 1174 GNUNET_assert (&melt_run == cmd.run); 1175 rms = cmd.cls; 1176 rms->do_retry = NUM_RETRIES; 1177 return cmd; 1178 } 1179 1180 1181 /** 1182 * Offer internal data from a "refresh reveal" CMD. 1183 * 1184 * @param cls closure. 1185 * @param[out] ret result (could be anything). 1186 * @param trait name of the trait. 1187 * @param index index number of the object to offer. 1188 * @return #GNUNET_OK on success. 1189 */ 1190 static enum GNUNET_GenericReturnValue 1191 melt_reveal_traits (void *cls, 1192 const void **ret, 1193 const char *trait, 1194 unsigned int index) 1195 { 1196 struct RevealMeltState *rrs = cls; 1197 1198 if (index >= rrs->num_fresh_coins) 1199 return GNUNET_SYSERR; 1200 1201 { 1202 struct TALER_TESTING_Trait traits[] = { 1203 TALER_TESTING_make_trait_coin_priv ( 1204 index, 1205 &rrs->fresh_coins[index].coin_priv), 1206 TALER_TESTING_make_trait_age_commitment_proof ( 1207 index, 1208 rrs->fresh_coins[index].age_commitment_proof), 1209 TALER_TESTING_make_trait_h_age_commitment ( 1210 index, 1211 &rrs->fresh_coins[index].h_age_commitment), 1212 TALER_TESTING_make_trait_denom_pub ( 1213 index, 1214 rrs->fresh_coins[index].pk), 1215 TALER_TESTING_make_trait_denom_sig ( 1216 index, 1217 &rrs->fresh_coins[index].sig), 1218 TALER_TESTING_make_trait_blinding_key ( 1219 index, 1220 &rrs->fresh_coins[index].blinding_key), 1221 TALER_TESTING_make_trait_array_length ( 1222 &rrs->num_fresh_coins), 1223 TALER_TESTING_make_trait_fresh_coins ( 1224 (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins), 1225 TALER_TESTING_make_trait_planchet_secrets (index, 1226 &rrs->psa[index]), 1227 TALER_TESTING_trait_end () 1228 }; 1229 1230 return TALER_TESTING_get_trait (traits, 1231 ret, 1232 trait, 1233 index); 1234 } 1235 } 1236 1237 1238 struct TALER_TESTING_Command 1239 TALER_TESTING_cmd_melt_reveal (const char *label, 1240 const char *melt_reference, 1241 unsigned int expected_response_code) 1242 { 1243 struct RevealMeltState *rrs; 1244 1245 rrs = GNUNET_new (struct RevealMeltState); 1246 rrs->melt_reference = melt_reference; 1247 rrs->expected_response_code = expected_response_code; 1248 { 1249 struct TALER_TESTING_Command cmd = { 1250 .cls = rrs, 1251 .label = label, 1252 .run = &melt_reveal_run, 1253 .cleanup = &melt_reveal_cleanup, 1254 .traits = &melt_reveal_traits 1255 }; 1256 1257 return cmd; 1258 } 1259 } 1260 1261 1262 struct TALER_TESTING_Command 1263 TALER_TESTING_cmd_melt_reveal_with_retry (struct TALER_TESTING_Command cmd) 1264 { 1265 struct RevealMeltState *rrs; 1266 1267 GNUNET_assert (&melt_reveal_run == cmd.run); 1268 rrs = cmd.cls; 1269 rrs->do_retry = NUM_RETRIES; 1270 return cmd; 1271 }