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