exchange_api_post-melt.c (22339B)
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 return GNUNET_OK; 516 } 517 518 519 /** 520 * The melt request @a mh failed, return an error to 521 * the application and cancel the operation. 522 * 523 * @param[in] mh melt request that failed 524 * @param ec error code to fail with 525 */ 526 static void 527 fail_mh (struct TALER_EXCHANGE_PostMeltHandle *mh, 528 enum TALER_ErrorCode ec) 529 { 530 struct TALER_EXCHANGE_PostMeltResponse mr = { 531 .hr.ec = ec 532 }; 533 534 mh->melt_cb (mh->melt_cb_cls, 535 &mr); 536 TALER_EXCHANGE_post_melt_cancel (mh); 537 } 538 539 540 /** 541 * Callbacks of this type are used to serve the result of submitting a 542 * /blinding-prepare request to a exchange. 543 * 544 * @param cls closure with our `struct TALER_EXCHANGE_MeltHandle *` 545 * @param bpr response details 546 */ 547 static void 548 blinding_prepare_cb ( 549 void *cls, 550 const struct TALER_EXCHANGE_PostBlindingPrepareResponse *bpr) 551 { 552 struct TALER_EXCHANGE_PostMeltHandle *mh = cls; 553 unsigned int nks_off = 0; 554 555 mh->bpr = NULL; 556 if (MHD_HTTP_OK != bpr->hr.http_status) 557 { 558 struct TALER_EXCHANGE_PostMeltResponse mr = { 559 .hr = bpr->hr 560 }; 561 562 mr.hr.hint = "/blinding-prepare failed"; 563 mh->melt_cb (mh->melt_cb_cls, 564 &mr); 565 TALER_EXCHANGE_post_melt_cancel (mh); 566 return; 567 } 568 for (unsigned int i = 0; i<mh->rd->num_fresh_denom_pubs; i++) 569 { 570 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 571 &mh->rd->fresh_denom_pubs[i]; 572 struct TALER_ExchangeBlindingValues *wv = &mh->melt_blinding_values[i]; 573 574 switch (fresh_pk->key.bsign_pub_key->cipher) 575 { 576 case GNUNET_CRYPTO_BSA_INVALID: 577 GNUNET_break (0); 578 fail_mh (mh, 579 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 580 return; 581 case GNUNET_CRYPTO_BSA_RSA: 582 break; 583 case GNUNET_CRYPTO_BSA_CS: 584 TALER_denom_ewv_copy (wv, 585 &bpr->details.ok.blinding_values[nks_off]); 586 nks_off++; 587 break; 588 } 589 } 590 if (GNUNET_OK != 591 start_melt (mh)) 592 { 593 GNUNET_break (0); 594 fail_mh (mh, 595 TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR); 596 return; 597 } 598 } 599 600 601 struct TALER_EXCHANGE_PostMeltHandle * 602 TALER_EXCHANGE_post_melt_create ( 603 struct GNUNET_CURL_Context *ctx, 604 const char *url, 605 struct TALER_EXCHANGE_Keys *keys, 606 const struct TALER_PublicRefreshMasterSeedP *rms, 607 const struct TALER_EXCHANGE_MeltInput *rd) 608 { 609 struct TALER_EXCHANGE_PostMeltHandle *mh; 610 611 if (0 == rd->num_fresh_denom_pubs) 612 { 613 GNUNET_break (0); 614 return NULL; 615 } 616 mh = GNUNET_new (struct TALER_EXCHANGE_PostMeltHandle); 617 mh->noreveal_index = TALER_CNC_KAPPA; /* invalid value */ 618 mh->cctx = ctx; 619 mh->exchange_url = GNUNET_strdup (url); 620 mh->rd = rd; 621 mh->rms = *rms; 622 mh->no_blinding_seed = true; 623 mh->melt_blinding_values = 624 GNUNET_new_array (rd->num_fresh_denom_pubs, 625 struct TALER_ExchangeBlindingValues); 626 for (unsigned int i = 0; i < rd->num_fresh_denom_pubs; i++) 627 { 628 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 629 &rd->fresh_denom_pubs[i]; 630 631 switch (fresh_pk->key.bsign_pub_key->cipher) 632 { 633 case GNUNET_CRYPTO_BSA_INVALID: 634 GNUNET_break (0); 635 GNUNET_free (mh->melt_blinding_values); 636 GNUNET_free (mh->exchange_url); 637 GNUNET_free (mh); 638 return NULL; 639 case GNUNET_CRYPTO_BSA_RSA: 640 TALER_denom_ewv_copy (&mh->melt_blinding_values[i], 641 TALER_denom_ewv_rsa_singleton ()); 642 break; 643 case GNUNET_CRYPTO_BSA_CS: 644 mh->no_blinding_seed = false; 645 break; 646 } 647 } 648 mh->keys = TALER_EXCHANGE_keys_incref (keys); 649 return mh; 650 } 651 652 653 enum TALER_ErrorCode 654 TALER_EXCHANGE_post_melt_start ( 655 struct TALER_EXCHANGE_PostMeltHandle *mh, 656 TALER_EXCHANGE_PostMeltCallback melt_cb, 657 TALER_EXCHANGE_POST_MELT_RESULT_CLOSURE *melt_cb_cls) 658 { 659 mh->melt_cb = melt_cb; 660 mh->melt_cb_cls = melt_cb_cls; 661 662 if (! mh->no_blinding_seed) 663 { 664 struct TALER_EXCHANGE_NonceKey nks[ 665 GNUNET_NZL (mh->rd->num_fresh_denom_pubs)]; 666 unsigned int nks_off = 0; 667 668 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 669 { 670 const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk = 671 &mh->rd->fresh_denom_pubs[i]; 672 673 if (GNUNET_CRYPTO_BSA_CS == 674 fresh_pk->key.bsign_pub_key->cipher) 675 { 676 nks[nks_off].pk = fresh_pk; 677 nks[nks_off].cnc_num = i; 678 nks_off++; 679 } 680 } 681 TALER_cs_refresh_seed_to_blinding_seed ( 682 &mh->rms, 683 &mh->rd->melt_priv, 684 &mh->blinding_seed); 685 mh->bpr = TALER_EXCHANGE_post_blinding_prepare_for_melt_create ( 686 mh->cctx, 687 mh->exchange_url, 688 &mh->blinding_seed, 689 nks_off, 690 nks); 691 if (NULL == mh->bpr) 692 { 693 GNUNET_break (0); 694 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 695 } 696 { 697 enum TALER_ErrorCode ec; 698 699 ec = TALER_EXCHANGE_post_blinding_prepare_start (mh->bpr, 700 &blinding_prepare_cb, 701 mh); 702 if (TALER_EC_NONE != ec) 703 { 704 GNUNET_break (0); 705 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 706 mh->bpr = NULL; 707 return ec; 708 } 709 } 710 return TALER_EC_NONE; 711 } 712 if (GNUNET_OK != 713 start_melt (mh)) 714 { 715 GNUNET_break (0); 716 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 717 } 718 return TALER_EC_NONE; 719 } 720 721 722 void 723 TALER_EXCHANGE_post_melt_cancel (struct TALER_EXCHANGE_PostMeltHandle *mh) 724 { 725 for (unsigned int i = 0; i < mh->rd->num_fresh_denom_pubs; i++) 726 TALER_denom_ewv_free (&mh->melt_blinding_values[i]); 727 if (NULL != mh->job) 728 { 729 GNUNET_CURL_job_cancel (mh->job); 730 mh->job = NULL; 731 } 732 if (NULL != mh->bpr) 733 { 734 TALER_EXCHANGE_post_blinding_prepare_cancel (mh->bpr); 735 mh->bpr = NULL; 736 } 737 TALER_EXCHANGE_free_melt_data (&mh->md); /* does not free 'md' itself */ 738 GNUNET_free (mh->melt_blinding_values); 739 GNUNET_free (mh->url); 740 GNUNET_free (mh->exchange_url); 741 TALER_curl_easy_post_finished (&mh->ctx); 742 TALER_EXCHANGE_keys_decref (mh->keys); 743 GNUNET_free (mh); 744 } 745 746 747 /* end of exchange_api_melt.c */