exchange_api_post-purses-PURSE_PUB-create.c (22824B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2022-2026 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-purses-PURSE_PUB-create.c 19 * @brief Implementation of the client to create a purse with 20 * an initial set of deposits (and a contract) 21 * @author Christian Grothoff 22 */ 23 #include <jansson.h> 24 #include <microhttpd.h> /* just for HTTP status codes */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include <gnunet/gnunet_json_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "taler/taler_json_lib.h" 29 #include "exchange_api_handle.h" 30 #include "exchange_api_common.h" 31 #include "taler/taler_signatures.h" 32 #include "exchange_api_curl_defaults.h" 33 34 35 /** 36 * Information we track per deposited coin. 37 */ 38 struct Deposit 39 { 40 /** 41 * Coin's public key. 42 */ 43 struct TALER_CoinSpendPublicKeyP coin_pub; 44 45 /** 46 * Signature made with the coin. 47 */ 48 struct TALER_CoinSpendSignatureP coin_sig; 49 50 /** 51 * Coin's denomination. 52 */ 53 struct TALER_DenominationHashP h_denom_pub; 54 55 /** 56 * Signature proving the validity of the coin. 57 */ 58 struct TALER_DenominationSignature denom_sig; 59 60 /** 61 * Age restriction hash for the coin. 62 */ 63 struct TALER_AgeCommitmentHashP ahac; 64 65 /** 66 * How much did we say the coin contributed. 67 */ 68 struct TALER_Amount contribution; 69 70 /** 71 * Age attestation for this coin. Valid if @e have_age. 72 */ 73 struct TALER_AgeAttestationP attest; 74 75 /** 76 * True if this coin uses age attestation. 77 */ 78 bool have_age; 79 80 }; 81 82 83 /** 84 * @brief A purse create with deposit handle 85 */ 86 struct TALER_EXCHANGE_PostPursesCreateHandle 87 { 88 89 /** 90 * The curl context for this request. 91 */ 92 struct GNUNET_CURL_Context *ctx; 93 94 /** 95 * The base URL of the exchange. 96 */ 97 char *base_url; 98 99 /** 100 * The keys of the exchange this request handle will use 101 */ 102 struct TALER_EXCHANGE_Keys *keys; 103 104 /** 105 * The url for this request, set during _start. 106 */ 107 char *url; 108 109 /** 110 * Context for #TEH_curl_easy_post(). Keeps the data that must 111 * persist for Curl to make the upload. 112 */ 113 struct TALER_CURL_PostContext post_ctx; 114 115 /** 116 * Handle for the request. 117 */ 118 struct GNUNET_CURL_Job *job; 119 120 /** 121 * Function to call with the result. 122 */ 123 TALER_EXCHANGE_PostPursesCreateCallback cb; 124 125 /** 126 * Closure for @a cb. 127 */ 128 TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls; 129 130 /** 131 * Expected value in the purse after fees. 132 */ 133 struct TALER_Amount purse_value_after_fees; 134 135 /** 136 * Our encrypted contract (if we had any). 137 */ 138 struct TALER_EncryptedContract econtract; 139 140 /** 141 * Public key of the merge capability. 142 */ 143 struct TALER_PurseMergePublicKeyP merge_pub; 144 145 /** 146 * Private key of the purse which we are creating. 147 */ 148 struct TALER_PurseContractPrivateKeyP purse_priv; 149 150 /** 151 * Private key for the merge operation. 152 */ 153 struct TALER_PurseMergePrivateKeyP merge_priv; 154 155 /** 156 * Private key to decrypt the contract. 157 */ 158 struct TALER_ContractDiffiePrivateP contract_priv; 159 160 /** 161 * Contract terms for the payment. 162 */ 163 json_t *contract_terms; 164 165 /** 166 * Minimum age, as per @e contract_terms. 167 */ 168 uint32_t min_age; 169 170 /** 171 * Public key of the purse. 172 */ 173 struct TALER_PurseContractPublicKeyP purse_pub; 174 175 /** 176 * Signature for purse creation. 177 */ 178 struct TALER_PurseContractSignatureP purse_sig; 179 180 /** 181 * Hash over the purse's contract terms. 182 */ 183 struct TALER_PrivateContractHashP h_contract_terms; 184 185 /** 186 * When does the purse expire. 187 */ 188 struct GNUNET_TIME_Timestamp purse_expiration; 189 190 /** 191 * Array of @e num_deposit deposits. 192 */ 193 struct Deposit *deposits; 194 195 /** 196 * How many deposits did we make? 197 */ 198 unsigned int num_deposits; 199 200 struct 201 { 202 203 /** 204 * Whether we are uploading a contract. 205 */ 206 bool upload_contract; 207 208 } options; 209 210 }; 211 212 213 /** 214 * Function called when we're done processing the 215 * HTTP /purses/$PID/create request. 216 * 217 * @param cls the `struct TALER_EXCHANGE_PostPursesCreateHandle` 218 * @param response_code HTTP response code, 0 on error 219 * @param response parsed JSON result, NULL on error 220 */ 221 static void 222 handle_purse_create_deposit_finished (void *cls, 223 long response_code, 224 const void *response) 225 { 226 struct TALER_EXCHANGE_PostPursesCreateHandle *pch = cls; 227 const json_t *j = response; 228 struct TALER_EXCHANGE_PostPursesCreateResponse dr = { 229 .hr.reply = j, 230 .hr.http_status = (unsigned int) response_code 231 }; 232 const struct TALER_EXCHANGE_Keys *keys = pch->keys; 233 234 pch->job = NULL; 235 switch (response_code) 236 { 237 case 0: 238 dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 239 break; 240 case MHD_HTTP_OK: 241 { 242 struct GNUNET_JSON_Specification spec[] = { 243 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 244 &dr.details.ok.exchange_sig), 245 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 246 &dr.details.ok.exchange_pub), 247 GNUNET_JSON_spec_timestamp ("exchange_timestamp", 248 &dr.details.ok.exchange_timestamp), 249 TALER_JSON_spec_amount ("total_deposited", 250 pch->purse_value_after_fees.currency, 251 &dr.details.ok.total_deposited), 252 GNUNET_JSON_spec_end () 253 }; 254 255 if (GNUNET_OK != 256 GNUNET_JSON_parse (j, 257 spec, 258 NULL, NULL)) 259 { 260 GNUNET_break_op (0); 261 dr.hr.http_status = 0; 262 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 263 break; 264 } 265 if (GNUNET_OK != 266 TALER_EXCHANGE_test_signing_key (keys, 267 &dr.details.ok.exchange_pub)) 268 { 269 GNUNET_break_op (0); 270 dr.hr.http_status = 0; 271 dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID; 272 break; 273 } 274 if (GNUNET_OK != 275 TALER_exchange_online_purse_created_verify ( 276 dr.details.ok.exchange_timestamp, 277 pch->purse_expiration, 278 &pch->purse_value_after_fees, 279 &dr.details.ok.total_deposited, 280 &pch->purse_pub, 281 &pch->h_contract_terms, 282 &dr.details.ok.exchange_pub, 283 &dr.details.ok.exchange_sig)) 284 { 285 GNUNET_break_op (0); 286 dr.hr.http_status = 0; 287 dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID; 288 break; 289 } 290 } 291 break; 292 case MHD_HTTP_BAD_REQUEST: 293 /* This should never happen, either us or the exchange is buggy 294 (or API version conflict); just pass JSON reply to the application */ 295 dr.hr.ec = TALER_JSON_get_error_code (j); 296 dr.hr.hint = TALER_JSON_get_error_hint (j); 297 break; 298 case MHD_HTTP_FORBIDDEN: 299 dr.hr.ec = TALER_JSON_get_error_code (j); 300 dr.hr.hint = TALER_JSON_get_error_hint (j); 301 /* Nothing really to verify, exchange says one of the signatures is 302 invalid; as we checked them, this should never happen, we 303 should pass the JSON reply to the application */ 304 break; 305 case MHD_HTTP_NOT_FOUND: 306 dr.hr.ec = TALER_JSON_get_error_code (j); 307 dr.hr.hint = TALER_JSON_get_error_hint (j); 308 /* Nothing really to verify, this should never 309 happen, we should pass the JSON reply to the application */ 310 break; 311 case MHD_HTTP_CONFLICT: 312 { 313 dr.hr.ec = TALER_JSON_get_error_code (j); 314 switch (dr.hr.ec) 315 { 316 case TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA: 317 if (GNUNET_OK != 318 TALER_EXCHANGE_check_purse_create_conflict_ ( 319 &pch->purse_sig, 320 &pch->purse_pub, 321 j)) 322 { 323 GNUNET_break_op (0); 324 dr.hr.http_status = 0; 325 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 326 break; 327 } 328 break; 329 case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: 330 /* Nothing to check anymore here, proof needs to be 331 checked in the GET /coins/$COIN_PUB handler */ 332 break; 333 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 334 // FIXME #7267: write check (add to exchange_api_common!) */ 335 break; 336 case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA: 337 { 338 struct TALER_CoinSpendPublicKeyP coin_pub; 339 struct TALER_CoinSpendSignatureP coin_sig; 340 struct TALER_DenominationHashP h_denom_pub; 341 struct TALER_AgeCommitmentHashP phac; 342 bool found = false; 343 344 if (GNUNET_OK != 345 TALER_EXCHANGE_check_purse_coin_conflict_ ( 346 &pch->purse_pub, 347 pch->base_url, 348 j, 349 &h_denom_pub, 350 &phac, 351 &coin_pub, 352 &coin_sig)) 353 { 354 GNUNET_break_op (0); 355 dr.hr.http_status = 0; 356 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 357 break; 358 } 359 for (unsigned int i = 0; i<pch->num_deposits; i++) 360 { 361 struct Deposit *deposit = &pch->deposits[i]; 362 363 if (0 != 364 GNUNET_memcmp (&coin_pub, 365 &deposit->coin_pub)) 366 continue; 367 if (0 != 368 GNUNET_memcmp (&deposit->h_denom_pub, 369 &h_denom_pub)) 370 { 371 found = true; 372 break; 373 } 374 if (0 != 375 GNUNET_memcmp (&deposit->ahac, 376 &phac)) 377 { 378 found = true; 379 break; 380 } 381 if (0 == 382 GNUNET_memcmp (&coin_sig, 383 &deposit->coin_sig)) 384 { 385 GNUNET_break_op (0); 386 continue; 387 } 388 found = true; 389 break; 390 } 391 if (! found) 392 { 393 /* conflict is for a different coin! */ 394 GNUNET_break_op (0); 395 dr.hr.http_status = 0; 396 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 397 break; 398 } 399 } 400 break; 401 case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA: 402 if (GNUNET_OK != 403 TALER_EXCHANGE_check_purse_econtract_conflict_ ( 404 &pch->econtract.econtract_sig, 405 &pch->purse_pub, 406 j)) 407 { 408 GNUNET_break_op (0); 409 dr.hr.http_status = 0; 410 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 411 break; 412 } 413 break; 414 default: 415 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 416 "Unexpected error code %d for conflicting deposit\n", 417 dr.hr.ec); 418 GNUNET_break_op (0); 419 dr.hr.http_status = 0; 420 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 421 } /* switch on (ec) */ 422 } 423 break; 424 case MHD_HTTP_GONE: 425 /* could happen if denomination was revoked */ 426 /* Note: one might want to check /keys for revocation 427 signature here, alas tricky in case our /keys 428 is outdated => left to clients */ 429 dr.hr.ec = TALER_JSON_get_error_code (j); 430 dr.hr.hint = TALER_JSON_get_error_hint (j); 431 break; 432 case MHD_HTTP_INTERNAL_SERVER_ERROR: 433 dr.hr.ec = TALER_JSON_get_error_code (j); 434 dr.hr.hint = TALER_JSON_get_error_hint (j); 435 /* Server had an internal issue; we should retry, but this API 436 leaves this to the application */ 437 break; 438 default: 439 /* unexpected response code */ 440 dr.hr.ec = TALER_JSON_get_error_code (j); 441 dr.hr.hint = TALER_JSON_get_error_hint (j); 442 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 443 "Unexpected response code %u/%d for exchange deposit\n", 444 (unsigned int) response_code, 445 dr.hr.ec); 446 GNUNET_break_op (0); 447 break; 448 } 449 if (NULL != pch->cb) 450 { 451 pch->cb (pch->cb_cls, 452 &dr); 453 pch->cb = NULL; 454 } 455 TALER_EXCHANGE_post_purses_create_cancel (pch); 456 } 457 458 459 struct TALER_EXCHANGE_PostPursesCreateHandle * 460 TALER_EXCHANGE_post_purses_create_create ( 461 struct GNUNET_CURL_Context *ctx, 462 const char *url, 463 struct TALER_EXCHANGE_Keys *keys, 464 const struct TALER_PurseContractPrivateKeyP *purse_priv, 465 const struct TALER_PurseMergePrivateKeyP *merge_priv, 466 const struct TALER_ContractDiffiePrivateP *contract_priv, 467 const json_t *contract_terms, 468 unsigned int num_deposits, 469 const struct TALER_EXCHANGE_PurseDeposit deposits[static num_deposits]) 470 { 471 struct TALER_EXCHANGE_PostPursesCreateHandle *pch; 472 473 pch = GNUNET_new (struct TALER_EXCHANGE_PostPursesCreateHandle); 474 pch->ctx = ctx; 475 pch->base_url = GNUNET_strdup (url); 476 pch->purse_priv = *purse_priv; 477 pch->merge_priv = *merge_priv; 478 pch->contract_priv = *contract_priv; 479 pch->contract_terms = json_incref ((json_t *) contract_terms); 480 { 481 struct GNUNET_JSON_Specification spec[] = { 482 GNUNET_JSON_spec_timestamp ("pay_deadline", 483 &pch->purse_expiration), 484 TALER_JSON_spec_amount_any ("amount", 485 &pch->purse_value_after_fees), 486 GNUNET_JSON_spec_mark_optional ( 487 GNUNET_JSON_spec_uint32 ("minimum_age", 488 &pch->min_age), 489 NULL), 490 GNUNET_JSON_spec_end () 491 }; 492 493 if (GNUNET_OK != 494 GNUNET_JSON_parse (contract_terms, 495 spec, 496 NULL, NULL)) 497 { 498 GNUNET_break (0); 499 GNUNET_free (pch->base_url); 500 GNUNET_free (pch); 501 return NULL; 502 } 503 } 504 if (GNUNET_OK != 505 TALER_JSON_contract_hash (contract_terms, 506 &pch->h_contract_terms)) 507 { 508 GNUNET_break (0); 509 GNUNET_free (pch->base_url); 510 GNUNET_free (pch); 511 return NULL; 512 } 513 GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv, 514 &pch->purse_pub.eddsa_pub); 515 GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv, 516 &pch->merge_pub.eddsa_pub); 517 pch->num_deposits = num_deposits; 518 pch->deposits = GNUNET_new_array (num_deposits, 519 struct Deposit); 520 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 521 "Signing with URL `%s'\n", 522 url); 523 for (unsigned int i = 0; i<num_deposits; i++) 524 { 525 const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; 526 const struct TALER_AgeCommitmentProof *acp = deposit->age_commitment_proof; 527 struct Deposit *d = &pch->deposits[i]; 528 529 if (NULL != acp) 530 { 531 TALER_age_commitment_hash (&acp->commitment, 532 &d->ahac); 533 d->have_age = true; 534 if (GNUNET_OK != 535 TALER_age_commitment_attest (acp, 536 pch->min_age, 537 &d->attest)) 538 { 539 GNUNET_break (0); 540 GNUNET_array_grow (pch->deposits, 541 pch->num_deposits, 542 0); 543 GNUNET_free (pch->base_url); 544 GNUNET_free (pch); 545 return NULL; 546 } 547 } 548 d->contribution = deposit->amount; 549 d->h_denom_pub = deposit->h_denom_pub; 550 TALER_denom_sig_copy (&d->denom_sig, 551 &deposit->denom_sig); 552 GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv, 553 &d->coin_pub.eddsa_pub); 554 TALER_wallet_purse_deposit_sign ( 555 url, 556 &pch->purse_pub, 557 &deposit->amount, 558 &d->h_denom_pub, 559 &d->ahac, 560 &deposit->coin_priv, 561 &d->coin_sig); 562 } 563 pch->keys = TALER_EXCHANGE_keys_incref (keys); 564 return pch; 565 } 566 567 568 enum GNUNET_GenericReturnValue 569 TALER_EXCHANGE_post_purses_create_set_options_ ( 570 struct TALER_EXCHANGE_PostPursesCreateHandle *ppch, 571 unsigned int num_options, 572 const struct TALER_EXCHANGE_PostPursesCreateOptionValue options[]) 573 { 574 for (unsigned int i = 0; i < num_options; i++) 575 { 576 const struct TALER_EXCHANGE_PostPursesCreateOptionValue *opt = &options[i]; 577 578 switch (opt->option) 579 { 580 case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_END: 581 return GNUNET_OK; 582 case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_UPLOAD_CONTRACT: 583 ppch->options.upload_contract = true; 584 break; 585 } 586 } 587 return GNUNET_OK; 588 } 589 590 591 enum TALER_ErrorCode 592 TALER_EXCHANGE_post_purses_create_start ( 593 struct TALER_EXCHANGE_PostPursesCreateHandle *pch, 594 TALER_EXCHANGE_PostPursesCreateCallback cb, 595 TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls) 596 { 597 char arg_str[sizeof (pch->purse_pub) * 2 + 32]; 598 CURL *eh; 599 json_t *deposit_arr; 600 json_t *body; 601 602 pch->cb = cb; 603 pch->cb_cls = cb_cls; 604 deposit_arr = json_array (); 605 GNUNET_assert (NULL != deposit_arr); 606 607 for (unsigned int i = 0; i<pch->num_deposits; i++) 608 { 609 struct Deposit *d = &pch->deposits[i]; 610 struct TALER_AgeCommitmentHashP *aghp = NULL; 611 struct TALER_AgeAttestationP *attestp = NULL; 612 json_t *jdeposit; 613 614 if (d->have_age) 615 { 616 aghp = &d->ahac; 617 attestp = &d->attest; 618 } 619 jdeposit = GNUNET_JSON_PACK ( 620 GNUNET_JSON_pack_allow_null ( 621 GNUNET_JSON_pack_data_auto ("h_age_commitment", 622 aghp)), 623 GNUNET_JSON_pack_allow_null ( 624 GNUNET_JSON_pack_data_auto ("age_attestation", 625 attestp)), 626 TALER_JSON_pack_amount ("amount", 627 &d->contribution), 628 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 629 &d->h_denom_pub), 630 TALER_JSON_pack_denom_sig ("ub_sig", 631 &d->denom_sig), 632 GNUNET_JSON_pack_data_auto ("coin_sig", 633 &d->coin_sig), 634 GNUNET_JSON_pack_data_auto ("coin_pub", 635 &d->coin_pub)); 636 GNUNET_assert (0 == 637 json_array_append_new (deposit_arr, 638 jdeposit)); 639 640 } 641 642 if (pch->options.upload_contract) 643 { 644 TALER_CRYPTO_contract_encrypt_for_merge ( 645 &pch->purse_pub, 646 &pch->contract_priv, 647 &pch->merge_priv, 648 pch->contract_terms, 649 &pch->econtract.econtract, 650 &pch->econtract.econtract_size); 651 GNUNET_CRYPTO_ecdhe_key_get_public ( 652 &pch->contract_priv.ecdhe_priv, 653 &pch->econtract.contract_pub.ecdhe_pub); 654 TALER_wallet_econtract_upload_sign ( 655 pch->econtract.econtract, 656 pch->econtract.econtract_size, 657 &pch->econtract.contract_pub, 658 &pch->purse_priv, 659 &pch->econtract.econtract_sig); 660 } 661 TALER_wallet_purse_create_sign (pch->purse_expiration, 662 &pch->h_contract_terms, 663 &pch->merge_pub, 664 pch->min_age, 665 &pch->purse_value_after_fees, 666 &pch->purse_priv, 667 &pch->purse_sig); 668 669 body = GNUNET_JSON_PACK ( 670 TALER_JSON_pack_amount ("amount", 671 &pch->purse_value_after_fees), 672 GNUNET_JSON_pack_uint64 ("min_age", 673 pch->min_age), 674 GNUNET_JSON_pack_allow_null ( 675 TALER_JSON_pack_econtract ("econtract", 676 pch->options.upload_contract 677 ? &pch->econtract 678 : NULL)), 679 GNUNET_JSON_pack_data_auto ("purse_sig", 680 &pch->purse_sig), 681 GNUNET_JSON_pack_data_auto ("merge_pub", 682 &pch->merge_pub), 683 GNUNET_JSON_pack_data_auto ("h_contract_terms", 684 &pch->h_contract_terms), 685 GNUNET_JSON_pack_timestamp ("purse_expiration", 686 pch->purse_expiration), 687 GNUNET_JSON_pack_array_steal ("deposits", 688 deposit_arr)); 689 if (NULL == body) 690 { 691 GNUNET_break (0); 692 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 693 } 694 695 { 696 char pub_str[sizeof (pch->purse_pub) * 2]; 697 char *end; 698 699 end = GNUNET_STRINGS_data_to_string ( 700 &pch->purse_pub, 701 sizeof (pch->purse_pub), 702 pub_str, 703 sizeof (pub_str)); 704 *end = '\0'; 705 GNUNET_snprintf (arg_str, 706 sizeof (arg_str), 707 "purses/%s/create", 708 pub_str); 709 } 710 pch->url = TALER_url_join (pch->base_url, 711 arg_str, 712 NULL); 713 if (NULL == pch->url) 714 { 715 GNUNET_break (0); 716 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 717 } 718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 719 "URL for purse create with deposit: `%s'\n", 720 pch->url); 721 eh = TALER_EXCHANGE_curl_easy_get_ (pch->url); 722 if ( (NULL == eh) || 723 (GNUNET_OK != 724 TALER_curl_easy_post (&pch->post_ctx, 725 eh, 726 body)) ) 727 { 728 GNUNET_break (0); 729 if (NULL != eh) 730 curl_easy_cleanup (eh); 731 json_decref (body); 732 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 733 } 734 json_decref (body); 735 pch->job = GNUNET_CURL_job_add2 (pch->ctx, 736 eh, 737 pch->post_ctx.headers, 738 &handle_purse_create_deposit_finished, 739 pch); 740 if (NULL == pch->job) 741 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 742 return TALER_EC_NONE; 743 } 744 745 746 void 747 TALER_EXCHANGE_post_purses_create_cancel ( 748 struct TALER_EXCHANGE_PostPursesCreateHandle *pch) 749 { 750 if (NULL != pch->job) 751 { 752 GNUNET_CURL_job_cancel (pch->job); 753 pch->job = NULL; 754 } 755 json_decref (pch->contract_terms); 756 GNUNET_free (pch->econtract.econtract); 757 GNUNET_free (pch->base_url); 758 GNUNET_free (pch->url); 759 760 for (unsigned int i = 0; i<pch->num_deposits; i++) 761 { 762 struct Deposit *d = &pch->deposits[i]; 763 764 TALER_denom_sig_free (&d->denom_sig); 765 } 766 GNUNET_array_grow (pch->deposits, 767 pch->num_deposits, 768 0); 769 TALER_EXCHANGE_keys_decref (pch->keys); 770 TALER_curl_easy_post_finished (&pch->post_ctx); 771 GNUNET_free (pch); 772 } 773 774 775 /* end of exchange_api_post-purses-PURSE_PUB-create.c */