exchange_api_post-melt.c (22474B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file lib/exchange_api_post-melt.c 19 * @brief Implementation of the /melt request 20 * @author Özgür Kesim 21 */ 22 #include <jansson.h> 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_json_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "taler/taler_json_lib.h" 28 #include "exchange_api_common.h" 29 #include "exchange_api_handle.h" 30 #include "taler/taler_signatures.h" 31 #include "exchange_api_curl_defaults.h" 32 #include "exchange_api_refresh_common.h" 33 34 35 /** 36 * @brief A /melt Handle 37 */ 38 struct TALER_EXCHANGE_PostMeltHandle 39 { 40 41 /** 42 * The keys of the this request handle will use 43 */ 44 struct TALER_EXCHANGE_Keys *keys; 45 46 /** 47 * Hash of the denomination of the melted coin, filled by 48 * #melt_coin_lookup() when checking a conflict reply. 49 */ 50 struct TALER_DenominationHashP h_melt_denom_pub; 51 52 /** 53 * The url for this request. 54 */ 55 char *url; 56 57 /** 58 * The exchange base url. 59 */ 60 char *exchange_url; 61 62 /** 63 * Curl context. 64 */ 65 struct GNUNET_CURL_Context *cctx; 66 67 /** 68 * Context for #TEH_curl_easy_post(). Keeps the data that must 69 * persist for Curl to make the upload. 70 */ 71 struct TALER_CURL_PostContext ctx; 72 73 /** 74 * Handle for the request. 75 */ 76 struct GNUNET_CURL_Job *job; 77 78 /** 79 * Function to call with refresh melt failure results. 80 */ 81 TALER_EXCHANGE_PostMeltCallback melt_cb; 82 83 /** 84 * Closure for @e result_cb and @e melt_failure_cb. 85 */ 86 void *melt_cb_cls; 87 88 /** 89 * Actual information about the melt operation. 90 */ 91 struct MeltData md; 92 93 /** 94 * The seed for the melt operation. 95 */ 96 struct TALER_PublicRefreshMasterSeedP rms; 97 98 /** 99 * Details about the characteristics of the requested melt operation. 100 */ 101 const struct TALER_EXCHANGE_MeltInput *rd; 102 103 /** 104 * True, if no blinding_seed is needed (no CS denominations involved) 105 */ 106 bool no_blinding_seed; 107 108 /** 109 * If @e no_blinding_seed is false, the blinding seed for the intermediate 110 * call to /blinding-prepare, in order to retrieve the R-values from the 111 * exchange for the blind Clause-Schnorr signature. 112 */ 113 struct TALER_BlindingMasterSeedP blinding_seed; 114 115 /** 116 * Array of `num_fresh_denom_pubs` per-coin values 117 * returned from melt operation. 118 */ 119 struct TALER_ExchangeBlindingValues *melt_blinding_values; 120 121 /** 122 * Handle for the preflight request, or NULL. 123 */ 124 struct TALER_EXCHANGE_PostBlindingPrepareHandle *bpr; 125 126 /** 127 * Public key of the coin being melted. 128 */ 129 struct TALER_CoinSpendPublicKeyP coin_pub; 130 131 /** 132 * Signature affirming the melt. 133 */ 134 struct TALER_CoinSpendSignatureP coin_sig; 135 136 /** 137 * @brief Public information about the coin's denomination key 138 */ 139 const struct TALER_EXCHANGE_DenomPublicKey *dki; 140 141 /** 142 * Gamma value chosen by the exchange during melt. 143 */ 144 uint32_t noreveal_index; 145 146 }; 147 148 149 /** 150 * Verify that the signature on the "200 OK" response 151 * from the exchange is valid. 152 * 153 * @param[in,out] mh melt handle 154 * @param json json reply with the signature 155 * @param[out] exchange_pub public key of the exchange used for the signature 156 * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not 157 */ 158 static enum GNUNET_GenericReturnValue 159 verify_melt_signature_ok (struct TALER_EXCHANGE_PostMeltHandle *mh, 160 const json_t *json, 161 struct TALER_ExchangePublicKeyP *exchange_pub) 162 { 163 struct TALER_ExchangeSignatureP exchange_sig; 164 struct GNUNET_JSON_Specification spec[] = { 165 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 166 &exchange_sig), 167 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 168 exchange_pub), 169 GNUNET_JSON_spec_uint32 ("noreveal_index", 170 &mh->noreveal_index), 171 GNUNET_JSON_spec_end () 172 }; 173 174 if (GNUNET_OK != 175 GNUNET_JSON_parse (json, 176 spec, 177 NULL, NULL)) 178 { 179 GNUNET_break_op (0); 180 return GNUNET_SYSERR; 181 } 182 /* check that exchange signing key is permitted */ 183 if (GNUNET_OK != 184 TALER_EXCHANGE_test_signing_key (mh->keys, 185 exchange_pub)) 186 { 187 GNUNET_break_op (0); 188 return GNUNET_SYSERR; 189 } 190 191 /* check that noreveal index is in permitted range */ 192 if (TALER_CNC_KAPPA <= mh->noreveal_index) 193 { 194 GNUNET_break_op (0); 195 return GNUNET_SYSERR; 196 } 197 198 if (GNUNET_OK != 199 TALER_exchange_online_melt_confirmation_verify ( 200 &mh->md.rc, 201 mh->noreveal_index, 202 exchange_pub, 203 &exchange_sig)) 204 { 205 GNUNET_break_op (0); 206 return GNUNET_SYSERR; 207 } 208 return GNUNET_OK; 209 } 210 211 212 /** 213 * Check that @a coin_pub is the melted coin and report how we used it. 214 * 215 * @param cls a `struct TALER_EXCHANGE_PostMeltHandle *` 216 * @param coin_pub public key of the coin named in the conflict reply 217 * @param[out] h_denom_pub set to the hash of the denomination we used 218 * @param[out] h_age_commitment set to the age commitment hash we used, or NULL 219 * @return #GNUNET_OK if found, #GNUNET_NO if the coin is not ours 220 */ 221 static enum GNUNET_GenericReturnValue 222 melt_coin_lookup (void *cls, 223 const struct TALER_CoinSpendPublicKeyP *coin_pub, 224 const struct TALER_DenominationHashP **h_denom_pub, 225 const struct TALER_AgeCommitmentHashP **h_age_commitment) 226 { 227 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 228 229 struct TALER_CoinSpendPublicKeyP my_pub; 230 231 GNUNET_CRYPTO_eddsa_key_get_public ( 232 &mh->md.melted_coin.coin_priv.eddsa_priv, 233 &my_pub.eddsa_pub); 234 if (0 != GNUNET_memcmp (coin_pub, 235 &my_pub)) 236 return GNUNET_NO; 237 TALER_denom_pub_hash (&mh->md.melted_coin.pub_key, 238 &mh->h_melt_denom_pub); 239 *h_denom_pub = &mh->h_melt_denom_pub; 240 *h_age_commitment = mh->md.melted_coin.h_age_commitment; 241 return GNUNET_OK; 242 } 243 244 245 /** 246 * Function called when we're done processing the 247 * HTTP /melt request. 248 * 249 * @param cls the `struct TALER_EXCHANGE_MeltHandle` 250 * @param response_code HTTP response code, 0 on error 251 * @param response parsed JSON result, NULL on error 252 */ 253 static void 254 handle_melt_finished (void *cls, 255 long response_code, 256 const void *response) 257 { 258 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 259 const json_t *j = response; 260 struct TALER_EXCHANGE_PostMeltResponse mr = { 261 .hr.reply = j, 262 .hr.http_status = (unsigned int) response_code 263 }; 264 265 mh->job = NULL; 266 switch (response_code) 267 { 268 case 0: 269 mr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 270 break; 271 case MHD_HTTP_OK: 272 if (GNUNET_OK != 273 verify_melt_signature_ok (mh, 274 j, 275 &mr.details.ok.sign_key)) 276 { 277 GNUNET_break_op (0); 278 mr.hr.http_status = 0; 279 mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; 280 break; 281 } 282 mr.details.ok.noreveal_index = mh->noreveal_index; 283 mr.details.ok.rc = mh->md.rc; 284 mr.details.ok.num_melt_blinding_values = mh->rd->num_fresh_denom_pubs; 285 mr.details.ok.melt_blinding_values = mh->melt_blinding_values; 286 mr.details.ok.blinding_seed = mh->no_blinding_seed 287 ? NULL 288 : &mh->blinding_seed; 289 mh->melt_cb (mh->melt_cb_cls, 290 &mr); 291 mh->melt_cb = NULL; 292 break; 293 case MHD_HTTP_BAD_REQUEST: 294 /* This should never happen, either us or the exchange is buggy 295 (or API version conflict); just pass JSON reply to the application */ 296 mr.hr.ec = TALER_JSON_get_error_code (j); 297 mr.hr.hint = TALER_JSON_get_error_hint (j); 298 break; 299 case MHD_HTTP_CONFLICT: 300 mr.hr.ec = TALER_JSON_get_error_code (j); 301 mr.hr.hint = TALER_JSON_get_error_hint (j); 302 switch (mr.hr.ec) 303 { 304 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 305 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH: 306 if (GNUNET_OK != 307 TALER_EXCHANGE_check_coin_conflict_ (mh->keys, 308 mr.hr.ec, 309 j, 310 &melt_coin_lookup, 311 mh, 312 &mr.details.conflict)) 313 { 314 GNUNET_break_op (0); 315 mr.hr.http_status = 0; 316 mr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 317 } 318 break; 319 default: 320 /* insufficient funds: proof is the coin history, checked by 321 the application via GET /coins/$COIN_PUB/history */ 322 break; 323 } 324 break; 325 case MHD_HTTP_FORBIDDEN: 326 /* Nothing really to verify, exchange says one of the signatures is 327 invalid; assuming we checked them, this should never happen, we 328 should pass the JSON reply to the application */ 329 mr.hr.ec = TALER_JSON_get_error_code (j); 330 mr.hr.hint = TALER_JSON_get_error_hint (j); 331 break; 332 case MHD_HTTP_NOT_FOUND: 333 /* Nothing really to verify, this should never 334 happen, we should pass the JSON reply to the application */ 335 mr.hr.ec = TALER_JSON_get_error_code (j); 336 mr.hr.hint = TALER_JSON_get_error_hint (j); 337 break; 338 case MHD_HTTP_INTERNAL_SERVER_ERROR: 339 /* Server had an internal issue; we should retry, but this API 340 leaves this to the application */ 341 mr.hr.ec = TALER_JSON_get_error_code (j); 342 mr.hr.hint = TALER_JSON_get_error_hint (j); 343 break; 344 default: 345 /* unexpected response code */ 346 mr.hr.ec = TALER_JSON_get_error_code (j); 347 mr.hr.hint = TALER_JSON_get_error_hint (j); 348 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 349 "Unexpected response code %u/%d for exchange melt\n", 350 (unsigned int) response_code, 351 mr.hr.ec); 352 GNUNET_break_op (0); 353 break; 354 } 355 if (NULL != mh->melt_cb) 356 mh->melt_cb (mh->melt_cb_cls, 357 &mr); 358 TALER_EXCHANGE_free_coin_conflict_ (&mr.hr, 359 &mr.details.conflict); 360 TALER_EXCHANGE_post_melt_cancel (mh); 361 } 362 363 364 /** 365 * Start the actual melt operation, now that we have 366 * the exchange's input values. 367 * 368 * @param[in,out] mh melt operation to run 369 * @return #GNUNET_OK if we could start the operation 370 */ 371 static enum GNUNET_GenericReturnValue 372 start_melt (struct TALER_EXCHANGE_PostMeltHandle *mh) 373 { 374 json_t *j_request_body; 375 json_t *j_transfer_pubs; 376 json_t *j_coin_evs; 377 CURL *eh; 378 struct TALER_DenominationHashP h_denom_pub; 379 380 if (GNUNET_OK != 381 TALER_EXCHANGE_get_melt_data (&mh->rms, 382 mh->rd, 383 mh->no_blinding_seed 384 ? NULL 385 : &mh->blinding_seed, 386 mh->melt_blinding_values, 387 &mh->md)) 388 { 389 GNUNET_break (0); 390 return GNUNET_SYSERR; 391 } 392 TALER_denom_pub_hash ( 393 &mh->md.melted_coin.pub_key, 394 &h_denom_pub); 395 TALER_wallet_melt_sign ( 396 &mh->md.melted_coin.melt_amount_with_fee, 397 &mh->md.melted_coin.fee_melt, 398 &mh->md.rc, 399 &h_denom_pub, 400 mh->md.melted_coin.h_age_commitment, 401 &mh->md.melted_coin.coin_priv, 402 &mh->coin_sig); 403 GNUNET_CRYPTO_eddsa_key_get_public ( 404 &mh->md.melted_coin.coin_priv.eddsa_priv, 405 &mh->coin_pub.eddsa_pub); 406 mh->dki = TALER_EXCHANGE_get_denomination_key ( 407 mh->keys, 408 &mh->md.melted_coin.pub_key); 409 j_request_body = GNUNET_JSON_PACK ( 410 GNUNET_JSON_pack_data_auto ("old_coin_pub", 411 &mh->coin_pub), 412 GNUNET_JSON_pack_data_auto ("old_denom_pub_h", 413 &h_denom_pub), 414 TALER_JSON_pack_denom_sig ("old_denom_sig", 415 &mh->md.melted_coin.sig), 416 GNUNET_JSON_pack_data_auto ("confirm_sig", 417 &mh->coin_sig), 418 TALER_JSON_pack_amount ("value_with_fee", 419 &mh->md.melted_coin.melt_amount_with_fee), 420 GNUNET_JSON_pack_allow_null ( 421 (NULL != mh->md.melted_coin.h_age_commitment) 422 ? GNUNET_JSON_pack_data_auto ("old_age_commitment_h", 423 mh->md.melted_coin.h_age_commitment) 424 : GNUNET_JSON_pack_string ("old_age_commitment_h", 425 NULL)), 426 GNUNET_JSON_pack_data_auto ("refresh_seed", 427 &mh->md.refresh_seed), 428 GNUNET_JSON_pack_allow_null ( 429 (mh->md.no_blinding_seed) 430 ? GNUNET_JSON_pack_string ("blinding_seed", 431 NULL) 432 : GNUNET_JSON_pack_data_auto ("blinding_seed", 433 &mh->md.blinding_seed)), 434 TALER_JSON_pack_array_of_data_auto ("denoms_h", 435 mh->md.num_fresh_coins, 436 mh->md.denoms_h) 437 ); 438 GNUNET_assert (NULL != j_request_body); 439 GNUNET_assert (NULL != 440 (j_transfer_pubs = json_array ())); 441 GNUNET_assert (NULL != 442 (j_coin_evs = json_array ())); 443 /** 444 * Fill the kappa array of coin envelopes and 445 * the array of transfer pubs. 446 */ 447 for (uint8_t k=0; k<TALER_CNC_KAPPA; k++) 448 { 449 json_t *j_envs; 450 json_t *j_tbs = GNUNET_JSON_PACK ( 451 TALER_JSON_pack_array_of_data_auto ("_", 452 mh->md.num_fresh_coins, 453 mh->md.kappa_transfer_pubs[k]) 454 ); 455 456 GNUNET_assert (NULL != (j_envs = json_array ())); 457 GNUNET_assert (NULL != j_tbs); 458 459 for (size_t i = 0; i < mh->md.num_fresh_coins; i++) 460 { 461 json_t *j_coin = GNUNET_JSON_PACK ( 462 TALER_JSON_pack_blinded_planchet (NULL, 463 &mh->md.kappa_blinded_planchets[k][i]) 464 ); 465 GNUNET_assert (NULL != j_coin); 466 GNUNET_assert (0 == 467 json_array_append_new (j_envs, 468 j_coin)); 469 } 470 GNUNET_assert (0 == 471 json_array_append_new (j_coin_evs, 472 j_envs)); 473 GNUNET_assert (0 == 474 json_array_append (j_transfer_pubs, 475 json_object_get (j_tbs, 476 "_"))); 477 json_decref (j_tbs); 478 } 479 GNUNET_assert (0 == 480 json_object_set_new (j_request_body, 481 "coin_evs", 482 j_coin_evs)); 483 GNUNET_assert (0 == 484 json_object_set_new (j_request_body, 485 "transfer_pubs", 486 j_transfer_pubs)); 487 /* and now we can at last begin the actual request handling */ 488 mh->url = TALER_url_join (mh->exchange_url, 489 "melt", 490 NULL); 491 if (NULL == mh->url) 492 { 493 json_decref (j_request_body); 494 return GNUNET_SYSERR; 495 } 496 eh = TALER_EXCHANGE_curl_easy_get_ (mh->url); 497 if ( (NULL == eh) || 498 (GNUNET_OK != 499 TALER_curl_easy_post (&mh->ctx, 500 eh, 501 j_request_body)) ) 502 { 503 GNUNET_break (0); 504 if (NULL != eh) 505 curl_easy_cleanup (eh); 506 json_decref (j_request_body); 507 return GNUNET_SYSERR; 508 } 509 json_decref (j_request_body); 510 mh->job = GNUNET_CURL_job_add2 (mh->cctx, 511 eh, 512 mh->ctx.headers, 513 &handle_melt_finished, 514 mh); 515 if (NULL == mh->job) 516 { 517 GNUNET_break (0); 518 /* GNUNET_CURL_job_add2() already released @a eh */ 519 return GNUNET_SYSERR; 520 } 521 return GNUNET_OK; 522 } 523 524 525 /** 526 * The melt request @a mh failed, return an error to 527 * the application and cancel the operation. 528 * 529 * @param[in] mh melt request that failed 530 * @param ec error code to fail with 531 */ 532 static void 533 fail_mh (struct TALER_EXCHANGE_PostMeltHandle *mh, 534 enum TALER_ErrorCode ec) 535 { 536 struct TALER_EXCHANGE_PostMeltResponse mr = { 537 .hr.ec = ec 538 }; 539 540 mh->melt_cb (mh->melt_cb_cls, 541 &mr); 542 TALER_EXCHANGE_post_melt_cancel (mh); 543 } 544 545 546 /** 547 * Callbacks of this type are used to serve the result of submitting a 548 * /blinding-prepare request to a exchange. 549 * 550 * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` 551 * @param bpr response details 552 */ 553 static void 554 blinding_prepare_cb ( 555 void *cls, 556 const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr) 557 { 558 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 559 unsigned int nks_off = 0; 560 561 mh->bpr = NULL; 562 if (MHD_HTTP_OK != bpr->hr.http_status) 563 { 564 struct TALER_EXCHANGE_PostMeltResponse mr = { 565 .hr = bpr->hr 566 }; 567 568 mr.hr.hint = "/blinding-prepare failed"; 569 mh->melt_cb (mh->melt_cb_cls, 570 &mr); 571 TALER_EXCHANGE_post_melt_cancel (mh); 572 return; 573 } 574 for (unsigned int i = 0; i<mh->rd->num_fresh_denom_pubs; i++) 575 { 576 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 577 &mh->rd->fresh_denom_pubs[i]; 578 struct TALER_ExchangeBlindingValues *wv = &mh->melt_blinding_values[i]; 579 580 switch (fresh_pk->key.bsign_pub_key->cipher) 581 { 582 case GNUNET_CRYPTO_BSA_INVALID: 583 GNUNET_break (0); 584 fail_mh (mh, 585 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 586 return; 587 case GNUNET_CRYPTO_BSA_RSA: 588 break; 589 case GNUNET_CRYPTO_BSA_CS: 590 TALER_denom_ewv_copy (wv, 591 &bpr->details.ok.blinding_values[nks_off]); 592 nks_off++; 593 break; 594 } 595 } 596 if (GNUNET_OK != 597 start_melt (mh)) 598 { 599 GNUNET_break (0); 600 fail_mh (mh, 601 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 602 return; 603 } 604 } 605 606 607 struct TALER_EXCHANGE_PostMeltHandle * 608 TALER_EXCHANGE_post_melt_create ( 609 struct GNUNET_CURL_Context *ctx, 610 const char *url, 611 struct TALER_EXCHANGE_Keys *keys, 612 const struct TALER_PublicRefreshMasterSeedP *rms, 613 const struct TALER_EXCHANGE_MeltInput *rd) 614 { 615 struct TALER_EXCHANGE_PostMeltHandle *mh; 616 617 if (0 == rd->num_fresh_denom_pubs) 618 { 619 GNUNET_break (0); 620 return NULL; 621 } 622 mh = GNUNET_new (struct TALER_EXCHANGE_PostMeltHandle); 623 mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ 624 mh->cctx = ctx; 625 mh->exchange_url = GNUNET_strdup (url); 626 mh->rd = rd; 627 mh->rms = *rms; 628 mh->no_blinding_seed = true; 629 mh->melt_blinding_values = 630 GNUNET_new_array (rd->num_fresh_denom_pubs, 631 struct TALER_ExchangeBlindingValues); 632 for (unsigned int i = 0; i < rd->num_fresh_denom_pubs; i++) 633 { 634 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 635 &rd->fresh_denom_pubs[i]; 636 637 switch (fresh_pk->key.bsign_pub_key->cipher) 638 { 639 case GNUNET_CRYPTO_BSA_INVALID: 640 GNUNET_break (0); 641 GNUNET_free (mh->melt_blinding_values); 642 GNUNET_free (mh->exchange_url); 643 GNUNET_free (mh); 644 return NULL; 645 case GNUNET_CRYPTO_BSA_RSA: 646 TALER_denom_ewv_copy (&mh->melt_blinding_values[i], 647 TALER_denom_ewv_rsa_singleton ()); 648 break; 649 case GNUNET_CRYPTO_BSA_CS: 650 mh->no_blinding_seed = false; 651 break; 652 } 653 } 654 mh->keys = TALER_EXCHANGE_keys_incref (keys); 655 return mh; 656 } 657 658 659 enum TALER_ErrorCode 660 TALER_EXCHANGE_post_melt_start ( 661 struct TALER_EXCHANGE_PostMeltHandle *mh, 662 TALER_EXCHANGE_PostMeltCallback melt_cb, 663 TALER_EXCHANGE_POST_MELT_RESULT_CLOSURE *melt_cb_cls) 664 { 665 mh->melt_cb = melt_cb; 666 mh->melt_cb_cls = melt_cb_cls; 667 668 if (! mh->no_blinding_seed) 669 { 670 struct TALER_EXCHANGE_NonceKey nks[ 671 GNUNET_NZL (mh->rd->num_fresh_denom_pubs)]; 672 unsigned int nks_off = 0; 673 674 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 675 { 676 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 677 &mh->rd->fresh_denom_pubs[i]; 678 679 if (GNUNET_CRYPTO_BSA_CS == 680 fresh_pk->key.bsign_pub_key->cipher) 681 { 682 nks[nks_off].pk = fresh_pk; 683 nks[nks_off].cnc_num = i; 684 nks_off++; 685 } 686 } 687 TALER_cs_refresh_seed_to_blinding_seed ( 688 &mh->rms, 689 &mh->rd->melt_priv, 690 &mh->blinding_seed); 691 mh->bpr = TALER_EXCHANGE_post_blinding_prepare_for_melt_create ( 692 mh->cctx, 693 mh->exchange_url, 694 &mh->blinding_seed, 695 nks_off, 696 nks); 697 if (NULL == mh->bpr) 698 { 699 GNUNET_break (0); 700 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 701 } 702 { 703 enum TALER_ErrorCode ec; 704 705 ec = TALER_EXCHANGE_post_blinding_prepare_start (mh->bpr, 706 &blinding_prepare_cb, 707 mh); 708 if (TALER_EC_NONE != ec) 709 { 710 GNUNET_break (0); 711 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 712 mh->bpr = NULL; 713 return ec; 714 } 715 } 716 return TALER_EC_NONE; 717 } 718 if (GNUNET_OK != 719 start_melt (mh)) 720 { 721 GNUNET_break (0); 722 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 723 } 724 return TALER_EC_NONE; 725 } 726 727 728 void 729 TALER_EXCHANGE_post_melt_cancel (struct TALER_EXCHANGE_PostMeltHandle *mh) 730 { 731 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 732 TALER_denom_ewv_free (&mh->melt_blinding_values[i]); 733 if (NULL != mh->job) 734 { 735 GNUNET_CURL_job_cancel (mh->job); 736 mh->job = NULL; 737 } 738 if (NULL != mh->bpr) 739 { 740 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 741 mh->bpr = NULL; 742 } 743 TALER_EXCHANGE_free_melt_data (&mh->md); /* does not free 'md' itself */ 744 GNUNET_free (mh->melt_blinding_values); 745 GNUNET_free (mh->url); 746 GNUNET_free (mh->exchange_url); 747 TALER_curl_easy_post_finished (&mh->ctx); 748 TALER_EXCHANGE_keys_decref (mh->keys); 749 GNUNET_free (mh); 750 } 751 752 753 /* end of exchange_api_melt.c */