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