testing_api_cmd_age_withdraw.c (25072B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023-2025 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_age_withdraw.c 21 * @brief implements the withdraw command for age-restricted coins 22 * @author Özgür Kesim 23 */ 24 25 #include "taler/taler_json_lib.h" 26 #include <gnunet/gnunet_common.h> 27 #include <microhttpd.h> 28 #include <gnunet/gnunet_curl_lib.h> 29 #include "taler/taler_signatures.h" 30 struct AgeWithdrawState; 31 #define TALER_EXCHANGE_POST_WITHDRAW_RESULT_CLOSURE struct AgeWithdrawState 32 #include "taler/exchange/post-withdraw.h" 33 struct AgeRevealWithdrawState; 34 #define TALER_EXCHANGE_POST_REVEAL_WITHDRAW_RESULT_CLOSURE \ 35 struct AgeRevealWithdrawState 36 #include "taler/exchange/post-reveal-withdraw.h" 37 #include "taler/taler_testing_lib.h" 38 39 /* 40 * The output state of coin 41 */ 42 struct CoinOutputState 43 { 44 45 /** 46 * The calculated details during "withdraw", for the selected coin. 47 */ 48 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details; 49 50 /** 51 * The (wanted) value of the coin, MUST be the same as input.denom_pub.value; 52 */ 53 struct TALER_Amount amount; 54 55 }; 56 57 /** 58 * State for a "age withdraw" CMD: 59 */ 60 61 struct AgeWithdrawState 62 { 63 64 /** 65 * Interpreter state (during command) 66 */ 67 struct TALER_TESTING_Interpreter *is; 68 69 /** 70 * The age-withdraw handle 71 */ 72 struct TALER_EXCHANGE_PostWithdrawHandle *handle; 73 74 /** 75 * Exchange base URL. Only used as offered trait. 76 */ 77 char *exchange_url; 78 79 /** 80 * URI of the reserve we are withdrawing from. 81 */ 82 struct TALER_NormalizedPayto reserve_payto_uri; 83 84 /** 85 * Private key of the reserve we are withdrawing from. 86 */ 87 struct TALER_ReservePrivateKeyP reserve_priv; 88 89 /** 90 * Public key of the reserve we are withdrawing from. 91 */ 92 struct TALER_ReservePublicKeyP reserve_pub; 93 94 /** 95 * Which reserve should we withdraw from? 96 */ 97 const char *reserve_reference; 98 99 /** 100 * Expected HTTP response code to the request. 101 */ 102 unsigned int expected_response_code; 103 104 /** 105 * Age mask 106 */ 107 struct TALER_AgeMask mask; 108 109 /** 110 * The maximum age we commit to 111 */ 112 uint8_t max_age; 113 114 /** 115 * Number of coins to withdraw 116 */ 117 size_t num_coins; 118 119 /** 120 * The @e num_coins denomination public keys that are provided 121 * to the `TALER_EXCHANGE_post_withdraw()` API. 122 */ 123 struct TALER_EXCHANGE_DenomPublicKey *denoms_pub; 124 125 /** 126 * The master seed from which all the other seeds are derived from 127 */ 128 struct TALER_WithdrawMasterSeedP seed; 129 130 /** 131 * The #TALER_CNC_KAPPA seeds derived from @e seed 132 */ 133 struct TALER_KappaWithdrawMasterSeedP kappa_seed; 134 135 /** 136 * The master seed from which all the other seeds are derived from 137 */ 138 struct TALER_BlindingMasterSeedP blinding_seed; 139 140 /** 141 * The output state of @e num_coins coins, calculated during the 142 * "age-withdraw" operation. 143 */ 144 struct CoinOutputState *coin_outputs; 145 146 /** 147 * The index returned by the exchange for the "age-withdraw" operation, 148 * of the kappa coin candidates that we do not disclose and keep. 149 */ 150 uint8_t noreveal_index; 151 152 /** 153 * The hash of the commitment, needed for the reveal step. 154 */ 155 struct TALER_HashBlindedPlanchetsP planchets_h; 156 157 /** 158 * The hash of the selected blinded planchets 159 */ 160 struct TALER_HashBlindedPlanchetsP selected_h; 161 162 /** 163 * Set to the KYC requirement payto hash *if* the exchange replied with a 164 * request for KYC. 165 */ 166 struct TALER_NormalizedPaytoHashP h_payto; 167 168 /** 169 * Set to the KYC requirement row *if* the exchange replied with 170 * a request for KYC. 171 */ 172 uint64_t requirement_row; 173 174 /** 175 * Reserve history entry that corresponds to this withdraw. 176 * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL. 177 */ 178 struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; 179 }; 180 181 /** 182 * Callback for the "age-withdraw" operation; It checks that the response 183 * code is expected and store the exchange signature in the state. 184 * 185 * @param aws Closure of type `struct AgeWithdrawState *` 186 * @param response Response details 187 */ 188 static void 189 age_withdraw_cb ( 190 struct AgeWithdrawState *aws, 191 const struct TALER_EXCHANGE_PostWithdrawResponse *response) 192 { 193 struct TALER_TESTING_Interpreter *is = aws->is; 194 195 aws->handle = NULL; 196 if (aws->expected_response_code != response->hr.http_status) 197 { 198 TALER_TESTING_unexpected_status_with_body (is, 199 response->hr.http_status, 200 aws->expected_response_code, 201 response->hr.reply); 202 return; 203 } 204 205 switch (response->hr.http_status) 206 { 207 case MHD_HTTP_CREATED: 208 aws->noreveal_index = response->details.created.noreveal_index; 209 aws->planchets_h = response->details.created.planchets_h; 210 aws->selected_h = response->details.created.selected_h; 211 aws->reserve_history.details.withdraw.planchets_h = aws->planchets_h; 212 aws->reserve_history.details.withdraw.selected_h = aws->selected_h; 213 aws->reserve_history.details.withdraw.noreveal_index = aws->noreveal_index; 214 aws->kappa_seed = response->details.created.kappa_seed; 215 216 GNUNET_assert (aws->num_coins == response->details.created.num_coins); 217 for (size_t n = 0; n < aws->num_coins; n++) 218 { 219 aws->coin_outputs[n].details = response->details.created.coin_details[n]; 220 TALER_age_commitment_proof_deep_copy ( 221 &aws->coin_outputs[n].details.age_commitment_proof, 222 &response->details.created.coin_details[n].age_commitment_proof); 223 TALER_denom_ewv_copy ( 224 &aws->coin_outputs[n].details.blinding_values, 225 &response->details.created.coin_details[n].blinding_values); 226 } 227 break; 228 case MHD_HTTP_FORBIDDEN: 229 case MHD_HTTP_NOT_FOUND: 230 case MHD_HTTP_GONE: 231 /* nothing to check */ 232 break; 233 case MHD_HTTP_CONFLICT: 234 /* FIXME[oec]: Add this to the response-type and handle it here */ 235 break; 236 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 237 default: 238 /* Unsupported status code (by test harness) */ 239 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 240 "test command for age-withdraw not support status code %u, body:\n" 241 ">>%s<<\n", 242 response->hr.http_status, 243 json_dumps (response->hr.reply, JSON_INDENT (2))); 244 GNUNET_break (0); 245 break; 246 } 247 248 /* We are done with this command, pick the next one */ 249 TALER_TESTING_interpreter_next (is); 250 } 251 252 253 /** 254 * Run the command for age-withdraw. 255 */ 256 static void 257 age_withdraw_run ( 258 void *cls, 259 const struct TALER_TESTING_Command *cmd, 260 struct TALER_TESTING_Interpreter *is) 261 { 262 struct AgeWithdrawState *aws = cls; 263 struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is); 264 const struct TALER_ReservePrivateKeyP *rp; 265 const struct TALER_TESTING_Command *create_reserve; 266 const struct TALER_EXCHANGE_DenomPublicKey *dpk; 267 268 aws->is = is; 269 270 /* Prepare the reserve related data */ 271 create_reserve 272 = TALER_TESTING_interpreter_lookup_command ( 273 is, 274 aws->reserve_reference); 275 276 if (NULL == create_reserve) 277 { 278 GNUNET_break (0); 279 TALER_TESTING_interpreter_fail (is); 280 return; 281 } 282 if (GNUNET_OK != 283 TALER_TESTING_get_trait_reserve_priv (create_reserve, 284 &rp)) 285 { 286 GNUNET_break (0); 287 TALER_TESTING_interpreter_fail (is); 288 return; 289 } 290 if (NULL == aws->exchange_url) 291 aws->exchange_url 292 = GNUNET_strdup (TALER_TESTING_get_exchange_url (is)); 293 aws->reserve_priv = *rp; 294 GNUNET_CRYPTO_eddsa_key_get_public (&aws->reserve_priv.eddsa_priv, 295 &aws->reserve_pub.eddsa_pub); 296 aws->reserve_payto_uri 297 = TALER_reserve_make_payto (aws->exchange_url, 298 &aws->reserve_pub); 299 300 aws->denoms_pub = GNUNET_new_array (aws->num_coins, 301 struct TALER_EXCHANGE_DenomPublicKey); 302 303 GNUNET_CRYPTO_random_block (&aws->seed, 304 sizeof(aws->seed)); 305 306 for (unsigned int i = 0; i<aws->num_coins; i++) 307 { 308 struct TALER_EXCHANGE_DenomPublicKey *denom_pub = &aws->denoms_pub[i]; 309 struct CoinOutputState *cos = &aws->coin_outputs[i]; 310 311 /* Find denomination */ 312 dpk = TALER_TESTING_find_pk (keys, 313 &cos->amount, 314 true); /* _always_ use denominations with age-striction */ 315 if (NULL == dpk) 316 { 317 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 318 "Failed to determine denomination key for amount at %s\n", 319 (NULL != cmd) ? cmd->label : "<retried command>"); 320 GNUNET_break (0); 321 TALER_TESTING_interpreter_fail (is); 322 return; 323 } 324 325 /* We copy the denomination key, as re-querying /keys 326 * would free the old one. */ 327 *denom_pub = *dpk; 328 TALER_denom_pub_copy (&denom_pub->key, 329 &dpk->key); 330 331 /* Accumulate the expected total amount and fee for the history */ 332 if (i == 0) 333 { 334 GNUNET_assert (GNUNET_OK == 335 TALER_amount_set_zero ( 336 cos->amount.currency, 337 &aws->reserve_history.amount)); 338 GNUNET_assert (GNUNET_OK == 339 TALER_amount_set_zero ( 340 denom_pub->fees.withdraw.currency, 341 &aws->reserve_history.details.withdraw.fee)); 342 } 343 GNUNET_assert (0 <= 344 TALER_amount_add (&aws->reserve_history.amount, 345 &aws->reserve_history.amount, 346 &cos->amount)); 347 GNUNET_assert (0 <= 348 TALER_amount_add (&aws->reserve_history.amount, 349 &aws->reserve_history.amount, 350 &denom_pub->fees.withdraw)); 351 GNUNET_assert (0 <= 352 TALER_amount_add (&aws->reserve_history.details.withdraw.fee, 353 &aws->reserve_history.details.withdraw.fee, 354 &denom_pub->fees.withdraw)); 355 356 } 357 /* Save the expected history entry */ 358 aws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL; 359 aws->reserve_history.details.withdraw.age_restricted = true; 360 aws->reserve_history.details.withdraw.max_age = aws->max_age; 361 362 363 /* Execute the age-restricted variant of withdraw protocol */ 364 aws->handle = 365 TALER_EXCHANGE_post_withdraw_create ( 366 TALER_TESTING_interpreter_get_context (is), 367 TALER_TESTING_get_exchange_url (is), 368 keys, 369 rp, 370 aws->num_coins, 371 aws->denoms_pub, 372 &aws->seed, 373 aws->max_age); 374 if (NULL == aws->handle) 375 { 376 GNUNET_break (0); 377 TALER_TESTING_interpreter_fail (is); 378 return; 379 } 380 GNUNET_assert (GNUNET_OK == 381 TALER_EXCHANGE_post_withdraw_set_options ( 382 aws->handle, 383 TALER_EXCHANGE_post_withdraw_option_with_age_proof ( 384 aws->max_age))); 385 GNUNET_assert (TALER_EC_NONE == 386 TALER_EXCHANGE_post_withdraw_start (aws->handle, 387 &age_withdraw_cb, 388 aws)); 389 } 390 391 392 /** 393 * Free the state of a "age withdraw" CMD, and possibly cancel a 394 * pending operation thereof 395 * 396 * @param cls Closure of type `struct AgeWithdrawState` 397 * @param cmd The command being freed. 398 */ 399 static void 400 age_withdraw_cleanup ( 401 void *cls, 402 const struct TALER_TESTING_Command *cmd) 403 { 404 struct AgeWithdrawState *aws = cls; 405 406 if (NULL != aws->handle) 407 { 408 TALER_TESTING_command_incomplete (aws->is, 409 cmd->label); 410 TALER_EXCHANGE_post_withdraw_cancel (aws->handle); 411 aws->handle = NULL; 412 } 413 414 if (NULL != aws->denoms_pub) 415 { 416 for (size_t n = 0; n < aws->num_coins; n++) 417 TALER_denom_pub_free (&aws->denoms_pub[n].key); 418 419 GNUNET_free (aws->denoms_pub); 420 aws->denoms_pub = NULL; 421 } 422 423 if (NULL != aws->coin_outputs) 424 { 425 for (size_t n = 0; n < aws->num_coins; n++) 426 { 427 struct CoinOutputState *out = &aws->coin_outputs[n]; 428 TALER_age_commitment_proof_free (&out->details.age_commitment_proof); 429 TALER_denom_ewv_free (&out->details.blinding_values); 430 } 431 GNUNET_free (aws->coin_outputs); 432 aws->coin_outputs = NULL; 433 } 434 435 GNUNET_free (aws->exchange_url); 436 aws->exchange_url = NULL; 437 GNUNET_free (aws->reserve_payto_uri.normalized_payto); 438 aws->reserve_payto_uri.normalized_payto = NULL; 439 GNUNET_free (aws); 440 } 441 442 443 /** 444 * Offer internal data of a "age withdraw" CMD state to other commands. 445 * 446 * @param cls Closure of type `struct AgeWithdrawState` 447 * @param[out] ret result (could be anything) 448 * @param trait name of the trait 449 * @param idx index number of the object to offer. 450 * @return #GNUNET_OK on success 451 */ 452 static enum GNUNET_GenericReturnValue 453 age_withdraw_traits ( 454 void *cls, 455 const void **ret, 456 const char *trait, 457 unsigned int idx) 458 { 459 struct AgeWithdrawState *aws = cls; 460 struct CoinOutputState *out = &aws->coin_outputs[idx]; 461 struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *details = 462 &aws->coin_outputs[idx].details; 463 struct TALER_TESTING_Trait traits[] = { 464 /* history entry MUST be first due to response code logic below! */ 465 TALER_TESTING_make_trait_reserve_history (idx, 466 &aws->reserve_history), 467 TALER_TESTING_make_trait_denom_pub (idx, 468 &aws->denoms_pub[idx]), 469 TALER_TESTING_make_trait_reserve_priv (&aws->reserve_priv), 470 TALER_TESTING_make_trait_reserve_pub (&aws->reserve_pub), 471 TALER_TESTING_make_trait_withdraw_commitment (&aws->planchets_h), 472 TALER_TESTING_make_trait_amounts (idx, 473 &out->amount), 474 /* FIXME[oec]: add legal requirement to response and handle it here, as well 475 TALER_TESTING_make_trait_legi_requirement_row (&aws->requirement_row), 476 TALER_TESTING_make_trait_h_payto (&aws->h_payto), 477 */ 478 TALER_TESTING_make_trait_normalized_payto_uri (&aws->reserve_payto_uri), 479 TALER_TESTING_make_trait_exchange_url (aws->exchange_url), 480 TALER_TESTING_make_trait_coin_priv (idx, 481 &details->coin_priv), 482 TALER_TESTING_make_trait_withdraw_seed (&aws->seed), 483 /* FIXME[oec]: needed!? 484 TALER_TESTING_make_trait_planchet_secrets (idx, 485 &aws->secrets[k][idx]), 486 */ 487 TALER_TESTING_make_trait_blinding_key (idx, 488 &details->blinding_key), 489 TALER_TESTING_make_trait_exchange_blinding_values (idx, 490 &details->blinding_values 491 ), 492 TALER_TESTING_make_trait_age_commitment_proof ( 493 idx, 494 &details->age_commitment_proof), 495 TALER_TESTING_make_trait_h_age_commitment ( 496 idx, 497 &details->h_age_commitment), 498 TALER_TESTING_trait_end () 499 }; 500 501 if (idx >= aws->num_coins) 502 return GNUNET_NO; 503 504 return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK) 505 ? &traits[0] /* we have reserve history */ 506 : &traits[1], /* skip reserve history */ 507 ret, 508 trait, 509 idx); 510 } 511 512 513 struct TALER_TESTING_Command 514 TALER_TESTING_cmd_withdraw_with_age_proof (const char *label, 515 const char *reserve_reference, 516 uint8_t max_age, 517 unsigned int 518 expected_response_code, 519 const char *amount, 520 ...) 521 { 522 struct AgeWithdrawState *aws; 523 unsigned int cnt; 524 va_list ap; 525 526 aws = GNUNET_new (struct AgeWithdrawState); 527 aws->reserve_reference = reserve_reference; 528 aws->expected_response_code = expected_response_code; 529 aws->mask = TALER_testing_age_restriction_mask; 530 aws->max_age = TALER_get_lowest_age (&aws->mask, 531 max_age); 532 cnt = 1; 533 va_start (ap, amount); 534 while (NULL != (va_arg (ap, const char *))) 535 cnt++; 536 aws->num_coins = cnt; 537 aws->coin_outputs = GNUNET_new_array (cnt, 538 struct CoinOutputState); 539 va_end (ap); 540 va_start (ap, amount); 541 542 for (unsigned int i = 0; i<aws->num_coins; i++) 543 { 544 struct CoinOutputState *out = &aws->coin_outputs[i]; 545 if (GNUNET_OK != 546 TALER_string_to_amount (amount, 547 &out->amount)) 548 { 549 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 550 "Failed to parse amount `%s' at %s\n", 551 amount, 552 label); 553 GNUNET_assert (0); 554 } 555 /* move on to next vararg! */ 556 amount = va_arg (ap, const char *); 557 } 558 559 GNUNET_assert (NULL == amount); 560 va_end (ap); 561 562 { 563 struct TALER_TESTING_Command cmd = { 564 .cls = aws, 565 .label = label, 566 .run = &age_withdraw_run, 567 .cleanup = &age_withdraw_cleanup, 568 .traits = &age_withdraw_traits, 569 }; 570 571 return cmd; 572 } 573 } 574 575 576 /** 577 * The state for the age-withdraw-reveal operation 578 */ 579 struct AgeRevealWithdrawState 580 { 581 /** 582 * The reference to the CMD resembling the previous call to age-withdraw 583 */ 584 const char *age_withdraw_reference; 585 586 /** 587 * The state to the previous age-withdraw command 588 */ 589 const struct AgeWithdrawState *aws; 590 591 /** 592 * The expected response code from the call to the 593 * age-withdraw-reveal operation 594 */ 595 unsigned int expected_response_code; 596 597 /** 598 * Interpreter state (during command) 599 */ 600 struct TALER_TESTING_Interpreter *is; 601 602 /** 603 * The handle to the reveal-operation 604 */ 605 struct TALER_EXCHANGE_PostRevealWithdrawHandle *handle; 606 607 608 /** 609 * Number of coins, extracted form the age withdraw command 610 */ 611 size_t num_coins; 612 613 /** 614 * The signatures of the @e num_coins coins returned 615 */ 616 struct TALER_DenominationSignature *denom_sigs; 617 618 }; 619 620 621 /** 622 * Callback for the reveal response 623 * 624 * @param awrs Closure of type `struct AgeRevealWithdrawState` 625 * @param response The response 626 */ 627 static void 628 age_reveal_withdraw_cb ( 629 struct AgeRevealWithdrawState *awrs, 630 const struct TALER_EXCHANGE_PostRevealWithdrawResponse *response) 631 { 632 struct TALER_TESTING_Interpreter *is = awrs->is; 633 634 awrs->handle = NULL; 635 if (awrs->expected_response_code != response->hr.http_status) 636 { 637 TALER_TESTING_unexpected_status_with_body (is, 638 response->hr.http_status, 639 awrs->expected_response_code, 640 response->hr.reply); 641 return; 642 } 643 switch (response->hr.http_status) 644 { 645 case MHD_HTTP_OK: 646 { 647 const struct AgeWithdrawState *aws = awrs->aws; 648 649 GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs); 650 awrs->denom_sigs = GNUNET_new_array (awrs->num_coins, 651 struct TALER_DenominationSignature); 652 for (size_t n = 0; n < awrs->num_coins; n++) 653 { 654 GNUNET_assert (GNUNET_OK == 655 TALER_denom_sig_unblind ( 656 &awrs->denom_sigs[n], 657 &response->details.ok.blinded_denom_sigs[n], 658 &aws->coin_outputs[n].details.blinding_key, 659 &aws->coin_outputs[n].details.h_coin_pub, 660 &aws->coin_outputs[n].details.blinding_values, 661 &aws->denoms_pub[n].key)); 662 } 663 664 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 665 "age-withdraw reveal success!\n"); 666 } 667 break; 668 case MHD_HTTP_NOT_FOUND: 669 case MHD_HTTP_FORBIDDEN: 670 /* nothing to check */ 671 break; 672 /* FIXME[oec]: handle more cases !? */ 673 default: 674 /* Unsupported status code (by test harness) */ 675 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 676 "Age withdraw reveal test command does not support status code %u\n", 677 response->hr.http_status); 678 GNUNET_break (0); 679 break; 680 } 681 682 /* We are done with this command, pick the next one */ 683 TALER_TESTING_interpreter_next (is); 684 } 685 686 687 /** 688 * Run the command for age-withdraw-reveal 689 */ 690 static void 691 age_reveal_withdraw_run ( 692 void *cls, 693 const struct TALER_TESTING_Command *cmd, 694 struct TALER_TESTING_Interpreter *is) 695 { 696 struct AgeRevealWithdrawState *awrs = cls; 697 const struct TALER_TESTING_Command *age_withdraw_cmd; 698 const struct AgeWithdrawState *aws; 699 700 (void) cmd; 701 awrs->is = is; 702 703 /* 704 * Get the command and state for the previous call to "age witdraw" 705 */ 706 age_withdraw_cmd = 707 TALER_TESTING_interpreter_lookup_command (is, 708 awrs->age_withdraw_reference); 709 if (NULL == age_withdraw_cmd) 710 { 711 GNUNET_break (0); 712 TALER_TESTING_interpreter_fail (is); 713 return; 714 } 715 GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run); 716 aws = age_withdraw_cmd->cls; 717 awrs->aws = aws; 718 awrs->num_coins = aws->num_coins; 719 720 { 721 struct TALER_RevealWithdrawMasterSeedsP revealed_seeds; 722 size_t j = 0; 723 for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) 724 { 725 if (aws->noreveal_index == k) 726 continue; 727 728 revealed_seeds.tuple[j] = aws->kappa_seed.tuple[k]; 729 j++; 730 } 731 732 awrs->handle = 733 TALER_EXCHANGE_post_reveal_withdraw_create ( 734 TALER_TESTING_interpreter_get_context (is), 735 TALER_TESTING_get_exchange_url (is), 736 aws->num_coins, 737 &aws->planchets_h, 738 &revealed_seeds); 739 GNUNET_assert (NULL != awrs->handle); 740 GNUNET_assert (TALER_EC_NONE == 741 TALER_EXCHANGE_post_reveal_withdraw_start ( 742 awrs->handle, 743 &age_reveal_withdraw_cb, 744 awrs)); 745 } 746 } 747 748 749 /** 750 * Free the state of a "age-withdraw-reveal" CMD, and possibly 751 * cancel a pending operation thereof 752 * 753 * @param cls Closure of type `struct AgeRevealWithdrawState` 754 * @param cmd The command being freed. 755 */ 756 static void 757 age_reveal_withdraw_cleanup ( 758 void *cls, 759 const struct TALER_TESTING_Command *cmd) 760 { 761 struct AgeRevealWithdrawState *awrs = cls; 762 763 if (NULL != awrs->handle) 764 { 765 TALER_TESTING_command_incomplete (awrs->is, 766 cmd->label); 767 TALER_EXCHANGE_post_reveal_withdraw_cancel (awrs->handle); 768 awrs->handle = NULL; 769 } 770 if (NULL != awrs->denom_sigs) 771 { 772 for (size_t n = 0; n < awrs->num_coins; n++) 773 { 774 TALER_denom_sig_free (&awrs->denom_sigs[n]); 775 } 776 GNUNET_free (awrs->denom_sigs); 777 } 778 GNUNET_free (awrs); 779 } 780 781 782 /** 783 * Offer internal data of a "age withdraw reveal" CMD state to other commands. 784 * 785 * @param cls Closure of they `struct AgeRevealWithdrawState` 786 * @param[out] ret result (could be anything) 787 * @param trait name of the trait 788 * @param idx index number of the object to offer. 789 * @return #GNUNET_OK on success 790 */ 791 static enum GNUNET_GenericReturnValue 792 age_reveal_withdraw_traits ( 793 void *cls, 794 const void **ret, 795 const char *trait, 796 unsigned int idx) 797 { 798 struct AgeRevealWithdrawState *awrs = cls; 799 struct TALER_TESTING_Trait traits[] = { 800 TALER_TESTING_make_trait_denom_sig (idx, 801 &awrs->denom_sigs[idx]), 802 /* FIXME: shall we provide the traits from the previous 803 * call to "age withdraw" as well? */ 804 TALER_TESTING_trait_end () 805 }; 806 807 if (NULL == awrs->denom_sigs) 808 return GNUNET_NO; 809 if (idx >= awrs->num_coins) 810 return GNUNET_NO; 811 812 return TALER_TESTING_get_trait (traits, 813 ret, 814 trait, 815 idx); 816 } 817 818 819 struct TALER_TESTING_Command 820 TALER_TESTING_cmd_withdraw_reveal_age_proof ( 821 const char *label, 822 const char *age_withdraw_reference, 823 unsigned int expected_response_code) 824 { 825 struct AgeRevealWithdrawState *awrs = 826 GNUNET_new (struct AgeRevealWithdrawState); 827 828 awrs->age_withdraw_reference = age_withdraw_reference; 829 awrs->expected_response_code = expected_response_code; 830 { 831 struct TALER_TESTING_Command cmd = { 832 .cls = awrs, 833 .label = label, 834 .run = age_reveal_withdraw_run, 835 .cleanup = age_reveal_withdraw_cleanup, 836 .traits = age_reveal_withdraw_traits, 837 }; 838 839 return cmd; 840 } 841 } 842 843 844 /* end of testing_api_cmd_age_withdraw.c */