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