exchange_api_post-batch-deposit.c (27387B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-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-batch-deposit.c 19 * @brief Implementation of the /batch-deposit request of the exchange's HTTP API 20 * @author Sree Harsha Totakura <sreeharsha@totakura.in> 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 "taler/taler_auditor_service.h" 30 #include "exchange_api_common.h" 31 #include "exchange_api_handle.h" 32 #include "taler/taler_signatures.h" 33 #include "exchange_api_curl_defaults.h" 34 35 36 /** 37 * 1:#AUDITOR_CHANCE is the probability that we report deposits 38 * to the auditor. 39 * 40 * 20==5% of going to auditor. This is possibly still too high, but set 41 * deliberately this high for testing 42 */ 43 #define AUDITOR_CHANCE 20 44 45 46 /** 47 * Entry in list of ongoing interactions with an auditor. 48 */ 49 struct TEAH_AuditorInteractionEntry 50 { 51 /** 52 * DLL entry. 53 */ 54 struct TEAH_AuditorInteractionEntry *next; 55 56 /** 57 * DLL entry. 58 */ 59 struct TEAH_AuditorInteractionEntry *prev; 60 61 /** 62 * URL of our auditor. For logging. 63 */ 64 const char *auditor_url; 65 66 /** 67 * Interaction state. 68 */ 69 struct TALER_AUDITOR_DepositConfirmationHandle *dch; 70 71 /** 72 * Batch deposit this is for. 73 */ 74 struct TALER_EXCHANGE_PostBatchDepositHandle *dh; 75 }; 76 77 78 /** 79 * @brief A Batch Deposit Handle 80 */ 81 struct TALER_EXCHANGE_PostBatchDepositHandle 82 { 83 84 /** 85 * The keys of the exchange. 86 */ 87 struct TALER_EXCHANGE_Keys *keys; 88 89 /** 90 * Context for our curl request(s). 91 */ 92 struct GNUNET_CURL_Context *ctx; 93 94 /** 95 * The base URL of the exchange (used to build the endpoint URL in _start). 96 */ 97 char *base_url; 98 99 /** 100 * The full endpoint URL for this request (built in _start). 101 */ 102 char *url; 103 104 /** 105 * Context for #TEH_curl_easy_post(). Keeps the data that must 106 * persist for Curl to make the upload. 107 */ 108 struct TALER_CURL_PostContext post_ctx; 109 110 /** 111 * Handle for the request. 112 */ 113 struct GNUNET_CURL_Job *job; 114 115 /** 116 * Function to call with the result. 117 */ 118 TALER_EXCHANGE_PostBatchDepositCallback cb; 119 120 /** 121 * Closure for @a cb. 122 */ 123 void *cb_cls; 124 125 /** 126 * Details about the contract. 127 */ 128 struct TALER_EXCHANGE_DepositContractDetail dcd; 129 130 /** 131 * Array with details about the coins. 132 */ 133 struct TALER_EXCHANGE_CoinDepositDetail *cdds; 134 135 /** 136 * Hash of the merchant's wire details. 137 */ 138 struct TALER_MerchantWireHashP h_wire; 139 140 /** 141 * Hash over the extensions, or all zero. 142 */ 143 struct TALER_ExtensionPolicyHashP h_policy; 144 145 /** 146 * Time when this confirmation was generated / when the exchange received 147 * the deposit request. 148 */ 149 struct GNUNET_TIME_Timestamp exchange_timestamp; 150 151 /** 152 * Exchange signature, set for #auditor_cb. 153 */ 154 struct TALER_ExchangeSignatureP exchange_sig; 155 156 /** 157 * Head of DLL of interactions with this auditor. 158 */ 159 struct TEAH_AuditorInteractionEntry *ai_head; 160 161 /** 162 * Tail of DLL of interactions with this auditor. 163 */ 164 struct TEAH_AuditorInteractionEntry *ai_tail; 165 166 /** 167 * Result to return to the application once @e ai_head is empty. 168 */ 169 struct TALER_EXCHANGE_PostBatchDepositResponse dr; 170 171 /** 172 * Exchange signing public key, set for #auditor_cb. 173 */ 174 struct TALER_ExchangePublicKeyP exchange_pub; 175 176 /** 177 * Total amount deposited without fees as calculated by us. 178 */ 179 struct TALER_Amount total_without_fee; 180 181 /** 182 * Response object to free at the end. 183 */ 184 json_t *response; 185 186 /** 187 * The JSON body to POST (built during _create, used during _start). 188 */ 189 json_t *deposit_obj; 190 191 /** 192 * Chance that we will inform the auditor about the deposit 193 * is 1:n, where the value of this field is "n". 194 */ 195 unsigned int auditor_chance; 196 197 /** 198 * Length of the @e cdds array. 199 */ 200 unsigned int num_cdds; 201 202 /** 203 * Whether to verify the merchant signature on the contract terms. 204 */ 205 bool verify_merchant_sig; 206 207 }; 208 209 210 /** 211 * Finish batch deposit operation by calling the callback. 212 * 213 * @param[in] dh handle to finished batch deposit operation 214 */ 215 static void 216 finish_dh (struct TALER_EXCHANGE_PostBatchDepositHandle *dh) 217 { 218 dh->cb (dh->cb_cls, 219 &dh->dr); 220 TALER_EXCHANGE_post_batch_deposit_cancel (dh); 221 } 222 223 224 /** 225 * Function called with the result from our call to the 226 * auditor's /deposit-confirmation handler. 227 * 228 * @param cls closure of type `struct TEAH_AuditorInteractionEntry *` 229 * @param dcr response 230 */ 231 static void 232 acc_confirmation_cb ( 233 void *cls, 234 const struct TALER_AUDITOR_DepositConfirmationResponse *dcr) 235 { 236 struct TEAH_AuditorInteractionEntry *aie = cls; 237 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = aie->dh; 238 239 if (MHD_HTTP_OK != dcr->hr.http_status) 240 { 241 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 242 "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n", 243 aie->auditor_url, 244 dcr->hr.http_status, 245 dcr->hr.ec); 246 } 247 GNUNET_CONTAINER_DLL_remove (dh->ai_head, 248 dh->ai_tail, 249 aie); 250 GNUNET_free (aie); 251 if (NULL == dh->ai_head) 252 finish_dh (dh); 253 } 254 255 256 /** 257 * Function called for each auditor to give us a chance to possibly 258 * launch a deposit confirmation interaction. 259 * 260 * @param cls closure 261 * @param auditor_url base URL of the auditor 262 * @param auditor_pub public key of the auditor 263 */ 264 static void 265 auditor_cb (void *cls, 266 const char *auditor_url, 267 const struct TALER_AuditorPublicKeyP *auditor_pub) 268 { 269 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls; 270 const struct TALER_EXCHANGE_SigningPublicKey *spk; 271 struct TEAH_AuditorInteractionEntry *aie; 272 const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL ( 273 dh->num_cdds)]; 274 const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL ( 275 dh->num_cdds)]; 276 277 for (unsigned int i = 0; i<dh->num_cdds; i++) 278 { 279 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i]; 280 281 csigs[i] = &cdd->coin_sig; 282 cpubs[i] = &cdd->coin_pub; 283 } 284 285 if (0 != 286 GNUNET_CRYPTO_random_u32 (dh->auditor_chance)) 287 { 288 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 289 "Not providing deposit confirmation to auditor\n"); 290 return; 291 } 292 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 293 "Will provide deposit confirmation to auditor `%s'\n", 294 TALER_B2S (auditor_pub)); 295 spk = TALER_EXCHANGE_get_signing_key_info (dh->keys, 296 &dh->exchange_pub); 297 if (NULL == spk) 298 { 299 GNUNET_break_op (0); 300 return; 301 } 302 aie = GNUNET_new (struct TEAH_AuditorInteractionEntry); 303 aie->dh = dh; 304 aie->auditor_url = auditor_url; 305 aie->dch = TALER_AUDITOR_deposit_confirmation ( 306 dh->ctx, 307 auditor_url, 308 &dh->h_wire, 309 &dh->h_policy, 310 &dh->dcd.h_contract_terms, 311 dh->exchange_timestamp, 312 dh->dcd.wire_deadline, 313 dh->dcd.refund_deadline, 314 &dh->total_without_fee, 315 dh->num_cdds, 316 cpubs, 317 csigs, 318 &dh->dcd.merchant_pub, 319 &dh->exchange_pub, 320 &dh->exchange_sig, 321 &dh->keys->master_pub, 322 spk->valid_from, 323 spk->valid_until, 324 spk->valid_legal, 325 &spk->master_sig, 326 &acc_confirmation_cb, 327 aie); 328 if (NULL == aie->dch) 329 { 330 GNUNET_break (0); 331 GNUNET_free (aie); 332 return; 333 } 334 GNUNET_CONTAINER_DLL_insert (dh->ai_head, 335 dh->ai_tail, 336 aie); 337 } 338 339 340 /** 341 * Function called when we're done processing the 342 * HTTP /batch-deposit request. 343 * 344 * @param cls the `struct TALER_EXCHANGE_PostBatchDepositHandle` 345 * @param response_code HTTP response code, 0 on error 346 * @param response parsed JSON result, NULL on error 347 */ 348 static void 349 handle_deposit_finished (void *cls, 350 long response_code, 351 const void *response) 352 { 353 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls; 354 const json_t *j = response; 355 struct TALER_EXCHANGE_PostBatchDepositResponse *dr = &dh->dr; 356 357 dh->job = NULL; 358 dh->response = json_incref ((json_t*) j); 359 dr->hr.reply = dh->response; 360 dr->hr.http_status = (unsigned int) response_code; 361 switch (response_code) 362 { 363 case 0: 364 dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 365 break; 366 case MHD_HTTP_OK: 367 { 368 bool prev33; 369 struct GNUNET_JSON_Specification spec[] = { 370 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 371 &dh->exchange_sig), 372 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 373 &dh->exchange_pub), 374 GNUNET_JSON_spec_mark_optional ( 375 TALER_JSON_spec_amount ( 376 "accumulated_total_without_fee", 377 dh->total_without_fee.currency, 378 &dr->details.ok.accumulated_total_without_fee), 379 &prev33), 380 GNUNET_JSON_spec_mark_optional ( 381 TALER_JSON_spec_web_url ("transaction_base_url", 382 &dr->details.ok.transaction_base_url), 383 NULL), 384 GNUNET_JSON_spec_timestamp ("exchange_timestamp", 385 &dh->exchange_timestamp), 386 GNUNET_JSON_spec_end () 387 }; 388 389 if (GNUNET_OK != 390 GNUNET_JSON_parse (j, 391 spec, 392 NULL, NULL)) 393 { 394 GNUNET_break_op (0); 395 dr->hr.http_status = 0; 396 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 397 break; 398 } 399 if (GNUNET_OK != 400 TALER_EXCHANGE_test_signing_key (dh->keys, 401 &dh->exchange_pub)) 402 { 403 GNUNET_break_op (0); 404 dr->hr.http_status = 0; 405 dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; 406 break; 407 } 408 if (prev33) 409 dr->details.ok.accumulated_total_without_fee 410 = dh->total_without_fee; 411 if (1 == 412 TALER_amount_cmp (&dh->total_without_fee, 413 &dr->details.ok.accumulated_total_without_fee)) 414 { 415 /* Amount signed by exchange is SMALLER than what we deposited */ 416 GNUNET_break_op (0); 417 dr->hr.http_status = 0; 418 dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; 419 break; 420 } 421 { 422 const struct TALER_CoinSpendSignatureP *csigs[ 423 GNUNET_NZL (dh->num_cdds)]; 424 425 for (unsigned int i = 0; i<dh->num_cdds; i++) 426 csigs[i] = &dh->cdds[i].coin_sig; 427 if (GNUNET_OK != 428 TALER_exchange_online_deposit_confirmation_verify ( 429 &dh->dcd.h_contract_terms, 430 &dh->h_wire, 431 &dh->h_policy, 432 dh->exchange_timestamp, 433 dh->dcd.wire_deadline, 434 dh->dcd.refund_deadline, 435 &dr->details.ok.accumulated_total_without_fee, 436 dh->num_cdds, 437 csigs, 438 &dh->dcd.merchant_pub, 439 &dh->exchange_pub, 440 &dh->exchange_sig)) 441 { 442 GNUNET_break_op (0); 443 dr->hr.http_status = 0; 444 dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; 445 break; 446 } 447 } 448 dh->total_without_fee = dr->details.ok.accumulated_total_without_fee; 449 TALER_EXCHANGE_get_auditors_for_dc_ (dh->keys, 450 &auditor_cb, 451 dh); 452 } 453 dr->details.ok.exchange_sig = &dh->exchange_sig; 454 dr->details.ok.exchange_pub = &dh->exchange_pub; 455 dr->details.ok.deposit_timestamp = dh->exchange_timestamp; 456 break; 457 case MHD_HTTP_BAD_REQUEST: 458 /* This should never happen, either us or the exchange is buggy 459 (or API version conflict); just pass JSON reply to the application */ 460 dr->hr.ec = TALER_JSON_get_error_code (j); 461 dr->hr.hint = TALER_JSON_get_error_hint (j); 462 break; 463 case MHD_HTTP_FORBIDDEN: 464 dr->hr.ec = TALER_JSON_get_error_code (j); 465 dr->hr.hint = TALER_JSON_get_error_hint (j); 466 /* Nothing really to verify, exchange says one of the signatures is 467 invalid; as we checked them, this should never happen, we 468 should pass the JSON reply to the application */ 469 break; 470 case MHD_HTTP_NOT_FOUND: 471 dr->hr.ec = TALER_JSON_get_error_code (j); 472 dr->hr.hint = TALER_JSON_get_error_hint (j); 473 /* Nothing really to verify, this should never 474 happen, we should pass the JSON reply to the application */ 475 break; 476 case MHD_HTTP_CONFLICT: 477 { 478 dr->hr.ec = TALER_JSON_get_error_code (j); 479 dr->hr.hint = TALER_JSON_get_error_hint (j); 480 switch (dr->hr.ec) 481 { 482 case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: 483 { 484 struct GNUNET_JSON_Specification spec[] = { 485 GNUNET_JSON_spec_fixed_auto ( 486 "coin_pub", 487 &dr->details.conflict.details.insufficient_funds.coin_pub), 488 GNUNET_JSON_spec_fixed_auto ( 489 "h_denom_pub", 490 &dr->details.conflict.details.insufficient_funds.h_denom_pub), 491 GNUNET_JSON_spec_end () 492 }; 493 494 if (GNUNET_OK != 495 GNUNET_JSON_parse (j, 496 spec, 497 NULL, NULL)) 498 { 499 GNUNET_break_op (0); 500 dr->hr.http_status = 0; 501 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 502 break; 503 } 504 } 505 break; 506 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH: 507 { 508 struct GNUNET_JSON_Specification spec[] = { 509 GNUNET_JSON_spec_fixed_auto ( 510 "coin_pub", 511 &dr->details.conflict.details 512 .coin_conflicting_age_hash.coin_pub), 513 GNUNET_JSON_spec_fixed_auto ( 514 "h_denom_pub", 515 &dr->details.conflict.details 516 .coin_conflicting_age_hash.h_denom_pub), 517 GNUNET_JSON_spec_end () 518 }; 519 520 if (GNUNET_OK != 521 GNUNET_JSON_parse (j, 522 spec, 523 NULL, NULL)) 524 { 525 GNUNET_break_op (0); 526 dr->hr.http_status = 0; 527 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 528 break; 529 } 530 } 531 break; 532 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 533 { 534 struct GNUNET_JSON_Specification spec[] = { 535 GNUNET_JSON_spec_fixed_auto ( 536 "coin_pub", 537 &dr->details.conflict.details 538 .coin_conflicting_denomination_key.coin_pub), 539 GNUNET_JSON_spec_end () 540 }; 541 542 if (GNUNET_OK != 543 GNUNET_JSON_parse (j, 544 spec, 545 NULL, NULL)) 546 { 547 GNUNET_break_op (0); 548 dr->hr.http_status = 0; 549 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 550 break; 551 } 552 } 553 break; 554 case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT: 555 break; 556 default: 557 GNUNET_break_op (0); 558 break; 559 } 560 } 561 break; 562 case MHD_HTTP_GONE: 563 /* could happen if denomination was revoked */ 564 /* Note: one might want to check /keys for revocation 565 signature here, alas tricky in case our /keys 566 is outdated => left to clients */ 567 dr->hr.ec = TALER_JSON_get_error_code (j); 568 dr->hr.hint = TALER_JSON_get_error_hint (j); 569 break; 570 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 571 dr->hr.ec = TALER_JSON_get_error_code (j); 572 dr->hr.hint = TALER_JSON_get_error_hint (j); 573 if (GNUNET_OK != 574 TALER_EXCHANGE_parse_451 (&dr->details.unavailable_for_legal_reasons, 575 j)) 576 { 577 GNUNET_break_op (0); 578 dr->hr.http_status = 0; 579 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 580 break; 581 } 582 break; 583 case MHD_HTTP_INTERNAL_SERVER_ERROR: 584 dr->hr.ec = TALER_JSON_get_error_code (j); 585 dr->hr.hint = TALER_JSON_get_error_hint (j); 586 /* Server had an internal issue; we should retry, but this API 587 leaves this to the application */ 588 break; 589 default: 590 /* unexpected response code */ 591 dr->hr.ec = TALER_JSON_get_error_code (j); 592 dr->hr.hint = TALER_JSON_get_error_hint (j); 593 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 594 "Unexpected response code %u/%d for exchange deposit\n", 595 (unsigned int) response_code, 596 dr->hr.ec); 597 GNUNET_break_op (0); 598 break; 599 } 600 if (NULL != dh->ai_head) 601 return; 602 finish_dh (dh); 603 } 604 605 606 struct TALER_EXCHANGE_PostBatchDepositHandle * 607 TALER_EXCHANGE_post_batch_deposit_create ( 608 struct GNUNET_CURL_Context *ctx, 609 const char *url, 610 struct TALER_EXCHANGE_Keys *keys, 611 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 612 unsigned int num_cdds, 613 const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds], 614 enum TALER_ErrorCode *ec) 615 { 616 struct TALER_EXCHANGE_PostBatchDepositHandle *dh; 617 json_t *deposits; 618 const struct GNUNET_HashCode *wallet_data_hashp; 619 620 if (0 == num_cdds) 621 { 622 GNUNET_break (0); 623 *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 624 return NULL; 625 } 626 if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline, 627 >, 628 dcd->wire_deadline)) 629 { 630 GNUNET_break_op (0); 631 *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE; 632 return NULL; 633 } 634 dh = GNUNET_new (struct TALER_EXCHANGE_PostBatchDepositHandle); 635 dh->auditor_chance = AUDITOR_CHANCE; 636 dh->ctx = ctx; 637 dh->base_url = GNUNET_strdup (url); 638 dh->cdds = GNUNET_memdup (cdds, 639 num_cdds * sizeof (*cdds)); 640 dh->num_cdds = num_cdds; 641 dh->dcd = *dcd; 642 if (NULL != dcd->policy_details) 643 TALER_deposit_policy_hash (dcd->policy_details, 644 &dh->h_policy); 645 TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri, 646 &dcd->wire_salt, 647 &dh->h_wire); 648 deposits = json_array (); 649 GNUNET_assert (NULL != deposits); 650 GNUNET_assert (GNUNET_OK == 651 TALER_amount_set_zero (cdds[0].amount.currency, 652 &dh->total_without_fee)); 653 for (unsigned int i = 0; i<num_cdds; i++) 654 { 655 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i]; 656 const struct TALER_EXCHANGE_DenomPublicKey *dki; 657 const struct TALER_AgeCommitmentHashP *h_age_commitmentp; 658 struct TALER_Amount amount_without_fee; 659 660 dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 661 &cdd->h_denom_pub); 662 if (NULL == dki) 663 { 664 *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; 665 GNUNET_break_op (0); 666 json_decref (deposits); 667 GNUNET_free (dh->base_url); 668 GNUNET_free (dh->cdds); 669 GNUNET_free (dh); 670 return NULL; 671 } 672 if (0 > 673 TALER_amount_subtract (&amount_without_fee, 674 &cdd->amount, 675 &dki->fees.deposit)) 676 { 677 *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT; 678 GNUNET_break_op (0); 679 json_decref (deposits); 680 GNUNET_free (dh->base_url); 681 GNUNET_free (dh->cdds); 682 GNUNET_free (dh); 683 return NULL; 684 } 685 GNUNET_assert (0 <= 686 TALER_amount_add (&dh->total_without_fee, 687 &dh->total_without_fee, 688 &amount_without_fee)); 689 if (GNUNET_OK != 690 TALER_EXCHANGE_verify_deposit_signature_ (dcd, 691 &dh->h_policy, 692 &dh->h_wire, 693 cdd, 694 dki)) 695 { 696 *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID; 697 GNUNET_break_op (0); 698 json_decref (deposits); 699 GNUNET_free (dh->base_url); 700 GNUNET_free (dh->cdds); 701 GNUNET_free (dh); 702 return NULL; 703 } 704 if (GNUNET_is_zero (&cdd->h_age_commitment)) 705 h_age_commitmentp = NULL; 706 else 707 h_age_commitmentp = &cdd->h_age_commitment; 708 GNUNET_assert ( 709 0 == 710 json_array_append_new ( 711 deposits, 712 GNUNET_JSON_PACK ( 713 TALER_JSON_pack_amount ("contribution", 714 &cdd->amount), 715 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 716 &cdd->h_denom_pub), 717 TALER_JSON_pack_denom_sig ("ub_sig", 718 &cdd->denom_sig), 719 GNUNET_JSON_pack_data_auto ("coin_pub", 720 &cdd->coin_pub), 721 GNUNET_JSON_pack_allow_null ( 722 GNUNET_JSON_pack_data_auto ("h_age_commitment", 723 h_age_commitmentp)), 724 GNUNET_JSON_pack_data_auto ("coin_sig", 725 &cdd->coin_sig) 726 ))); 727 } 728 729 if (GNUNET_is_zero (&dcd->wallet_data_hash)) 730 wallet_data_hashp = NULL; 731 else 732 wallet_data_hashp = &dcd->wallet_data_hash; 733 dh->deposit_obj = GNUNET_JSON_PACK ( 734 TALER_JSON_pack_full_payto ("merchant_payto_uri", 735 dcd->merchant_payto_uri), 736 GNUNET_JSON_pack_allow_null ( 737 GNUNET_JSON_pack_string ("extra_wire_subject_metadata", 738 dcd->extra_wire_subject_metadata)), 739 GNUNET_JSON_pack_data_auto ("wire_salt", 740 &dcd->wire_salt), 741 GNUNET_JSON_pack_data_auto ("h_contract_terms", 742 &dcd->h_contract_terms), 743 GNUNET_JSON_pack_array_steal ("coins", 744 deposits), 745 GNUNET_JSON_pack_allow_null ( 746 GNUNET_JSON_pack_data_auto ("wallet_data_hash", 747 wallet_data_hashp)), 748 GNUNET_JSON_pack_allow_null ( 749 GNUNET_JSON_pack_object_steal ("policy", 750 (json_t *) dcd->policy_details)), 751 GNUNET_JSON_pack_timestamp ("timestamp", 752 dcd->wallet_timestamp), 753 GNUNET_JSON_pack_data_auto ("merchant_pub", 754 &dcd->merchant_pub), 755 GNUNET_JSON_pack_data_auto ("merchant_sig", 756 &dcd->merchant_sig), 757 GNUNET_JSON_pack_allow_null ( 758 GNUNET_JSON_pack_timestamp ("refund_deadline", 759 dcd->refund_deadline)), 760 GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", 761 dcd->wire_deadline)); 762 if (NULL == dh->deposit_obj) 763 { 764 GNUNET_break (0); 765 *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; 766 GNUNET_free (dh->base_url); 767 GNUNET_free (dh->cdds); 768 GNUNET_free (dh); 769 return NULL; 770 } 771 dh->keys = TALER_EXCHANGE_keys_incref (keys); 772 *ec = TALER_EC_NONE; 773 return dh; 774 } 775 776 777 enum GNUNET_GenericReturnValue 778 TALER_EXCHANGE_post_batch_deposit_set_options_ ( 779 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 780 unsigned int num_options, 781 const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[]) 782 { 783 for (unsigned int i = 0; i < num_options; i++) 784 { 785 const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i]; 786 switch (opt->option) 787 { 788 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END: 789 return GNUNET_OK; 790 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC: 791 pbdh->auditor_chance = 1; 792 break; 793 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG: 794 pbdh->verify_merchant_sig = true; 795 break; 796 } 797 } 798 return GNUNET_OK; 799 } 800 801 802 enum TALER_ErrorCode 803 TALER_EXCHANGE_post_batch_deposit_start ( 804 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 805 TALER_EXCHANGE_PostBatchDepositCallback cb, 806 TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls) 807 { 808 CURL *eh; 809 810 if (pbdh->verify_merchant_sig && 811 (GNUNET_OK != 812 TALER_merchant_contract_verify ( 813 &pbdh->dcd.h_contract_terms, 814 &pbdh->dcd.merchant_pub, 815 &pbdh->dcd.merchant_sig)) ) 816 { 817 GNUNET_break_op (0); 818 return TALER_EC_GENERIC_PARAMETER_MALFORMED; 819 } 820 pbdh->cb = cb; 821 pbdh->cb_cls = cb_cls; 822 pbdh->url = TALER_url_join (pbdh->base_url, 823 "batch-deposit", 824 NULL); 825 if (NULL == pbdh->url) 826 { 827 GNUNET_break (0); 828 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 829 } 830 eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url); 831 if ( (NULL == eh) || 832 (GNUNET_OK != 833 TALER_curl_easy_post (&pbdh->post_ctx, 834 eh, 835 pbdh->deposit_obj)) ) 836 { 837 GNUNET_break (0); 838 if (NULL != eh) 839 curl_easy_cleanup (eh); 840 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 841 } 842 /* Help debug #11305 */ 843 GNUNET_assert (CURLE_OK == 844 curl_easy_setopt (eh, 845 CURLOPT_VERBOSE, 846 1)); 847 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 848 "URL for batch-deposit: `%s'\n", 849 pbdh->url); 850 pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx, 851 eh, 852 pbdh->post_ctx.headers, 853 &handle_deposit_finished, 854 pbdh); 855 if (NULL == pbdh->job) 856 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 857 return TALER_EC_NONE; 858 } 859 860 861 void 862 TALER_EXCHANGE_post_batch_deposit_cancel ( 863 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh) 864 { 865 struct TEAH_AuditorInteractionEntry *aie; 866 867 while (NULL != (aie = pbdh->ai_head)) 868 { 869 GNUNET_assert (aie->dh == pbdh); 870 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 871 "Not sending deposit confirmation to auditor `%s' due to cancellation\n", 872 aie->auditor_url); 873 TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); 874 GNUNET_CONTAINER_DLL_remove (pbdh->ai_head, 875 pbdh->ai_tail, 876 aie); 877 GNUNET_free (aie); 878 } 879 if (NULL != pbdh->job) 880 { 881 GNUNET_CURL_job_cancel (pbdh->job); 882 pbdh->job = NULL; 883 } 884 TALER_EXCHANGE_keys_decref (pbdh->keys); 885 GNUNET_free (pbdh->base_url); 886 GNUNET_free (pbdh->url); 887 GNUNET_free (pbdh->cdds); 888 TALER_curl_easy_post_finished (&pbdh->post_ctx); 889 json_decref (pbdh->deposit_obj); 890 json_decref (pbdh->response); 891 GNUNET_free (pbdh); 892 } 893 894 895 /* end of exchange_api_post-batch-deposit.c */