exchange_api_post-batch-deposit.c (27447B)
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 (GNUNET_CRYPTO_QUALITY_WEAK, 287 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 if (NULL != dcd->policy_details) 644 TALER_deposit_policy_hash (dcd->policy_details, 645 &dh->h_policy); 646 TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri, 647 &dcd->wire_salt, 648 &dh->h_wire); 649 deposits = json_array (); 650 GNUNET_assert (NULL != deposits); 651 GNUNET_assert (GNUNET_OK == 652 TALER_amount_set_zero (cdds[0].amount.currency, 653 &dh->total_without_fee)); 654 for (unsigned int i = 0; i<num_cdds; i++) 655 { 656 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i]; 657 const struct TALER_EXCHANGE_DenomPublicKey *dki; 658 const struct TALER_AgeCommitmentHashP *h_age_commitmentp; 659 struct TALER_Amount amount_without_fee; 660 661 dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 662 &cdd->h_denom_pub); 663 if (NULL == dki) 664 { 665 *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; 666 GNUNET_break_op (0); 667 json_decref (deposits); 668 GNUNET_free (dh->base_url); 669 GNUNET_free (dh->cdds); 670 GNUNET_free (dh); 671 return NULL; 672 } 673 if (0 > 674 TALER_amount_subtract (&amount_without_fee, 675 &cdd->amount, 676 &dki->fees.deposit)) 677 { 678 *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT; 679 GNUNET_break_op (0); 680 json_decref (deposits); 681 GNUNET_free (dh->base_url); 682 GNUNET_free (dh->cdds); 683 GNUNET_free (dh); 684 return NULL; 685 } 686 GNUNET_assert (0 <= 687 TALER_amount_add (&dh->total_without_fee, 688 &dh->total_without_fee, 689 &amount_without_fee)); 690 if (GNUNET_OK != 691 TALER_EXCHANGE_verify_deposit_signature_ (dcd, 692 &dh->h_policy, 693 &dh->h_wire, 694 cdd, 695 dki)) 696 { 697 *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID; 698 GNUNET_break_op (0); 699 json_decref (deposits); 700 GNUNET_free (dh->base_url); 701 GNUNET_free (dh->cdds); 702 GNUNET_free (dh); 703 return NULL; 704 } 705 if (GNUNET_is_zero (&cdd->h_age_commitment)) 706 h_age_commitmentp = NULL; 707 else 708 h_age_commitmentp = &cdd->h_age_commitment; 709 GNUNET_assert ( 710 0 == 711 json_array_append_new ( 712 deposits, 713 GNUNET_JSON_PACK ( 714 TALER_JSON_pack_amount ("contribution", 715 &cdd->amount), 716 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 717 &cdd->h_denom_pub), 718 TALER_JSON_pack_denom_sig ("ub_sig", 719 &cdd->denom_sig), 720 GNUNET_JSON_pack_data_auto ("coin_pub", 721 &cdd->coin_pub), 722 GNUNET_JSON_pack_allow_null ( 723 GNUNET_JSON_pack_data_auto ("h_age_commitment", 724 h_age_commitmentp)), 725 GNUNET_JSON_pack_data_auto ("coin_sig", 726 &cdd->coin_sig) 727 ))); 728 } 729 730 if (GNUNET_is_zero (&dcd->wallet_data_hash)) 731 wallet_data_hashp = NULL; 732 else 733 wallet_data_hashp = &dcd->wallet_data_hash; 734 dh->deposit_obj = GNUNET_JSON_PACK ( 735 TALER_JSON_pack_full_payto ("merchant_payto_uri", 736 dcd->merchant_payto_uri), 737 GNUNET_JSON_pack_allow_null ( 738 GNUNET_JSON_pack_string ("extra_wire_subject_metadata", 739 dcd->extra_wire_subject_metadata)), 740 GNUNET_JSON_pack_data_auto ("wire_salt", 741 &dcd->wire_salt), 742 GNUNET_JSON_pack_data_auto ("h_contract_terms", 743 &dcd->h_contract_terms), 744 GNUNET_JSON_pack_array_steal ("coins", 745 deposits), 746 GNUNET_JSON_pack_allow_null ( 747 GNUNET_JSON_pack_data_auto ("wallet_data_hash", 748 wallet_data_hashp)), 749 GNUNET_JSON_pack_allow_null ( 750 GNUNET_JSON_pack_object_steal ("policy", 751 (json_t *) dcd->policy_details)), 752 GNUNET_JSON_pack_timestamp ("timestamp", 753 dcd->wallet_timestamp), 754 GNUNET_JSON_pack_data_auto ("merchant_pub", 755 &dcd->merchant_pub), 756 GNUNET_JSON_pack_data_auto ("merchant_sig", 757 &dcd->merchant_sig), 758 GNUNET_JSON_pack_allow_null ( 759 GNUNET_JSON_pack_timestamp ("refund_deadline", 760 dcd->refund_deadline)), 761 GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", 762 dcd->wire_deadline)); 763 if (NULL == dh->deposit_obj) 764 { 765 GNUNET_break (0); 766 *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; 767 GNUNET_free (dh->base_url); 768 GNUNET_free (dh->cdds); 769 GNUNET_free (dh); 770 return NULL; 771 } 772 dh->keys = TALER_EXCHANGE_keys_incref (keys); 773 *ec = TALER_EC_NONE; 774 return dh; 775 } 776 777 778 enum GNUNET_GenericReturnValue 779 TALER_EXCHANGE_post_batch_deposit_set_options_ ( 780 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 781 unsigned int num_options, 782 const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[]) 783 { 784 for (unsigned int i = 0; i < num_options; i++) 785 { 786 const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i]; 787 switch (opt->option) 788 { 789 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END: 790 return GNUNET_OK; 791 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC: 792 pbdh->auditor_chance = 1; 793 break; 794 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG: 795 pbdh->verify_merchant_sig = true; 796 break; 797 } 798 } 799 return GNUNET_OK; 800 } 801 802 803 enum TALER_ErrorCode 804 TALER_EXCHANGE_post_batch_deposit_start ( 805 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 806 TALER_EXCHANGE_PostBatchDepositCallback cb, 807 TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls) 808 { 809 CURL *eh; 810 811 if (pbdh->verify_merchant_sig && 812 (GNUNET_OK != 813 TALER_merchant_contract_verify ( 814 &pbdh->dcd.h_contract_terms, 815 &pbdh->dcd.merchant_pub, 816 &pbdh->dcd.merchant_sig)) ) 817 { 818 GNUNET_break_op (0); 819 return TALER_EC_GENERIC_PARAMETER_MALFORMED; 820 } 821 pbdh->cb = cb; 822 pbdh->cb_cls = cb_cls; 823 pbdh->url = TALER_url_join (pbdh->base_url, 824 "batch-deposit", 825 NULL); 826 if (NULL == pbdh->url) 827 { 828 GNUNET_break (0); 829 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 830 } 831 eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url); 832 if ( (NULL == eh) || 833 (GNUNET_OK != 834 TALER_curl_easy_post (&pbdh->post_ctx, 835 eh, 836 pbdh->deposit_obj)) ) 837 { 838 GNUNET_break (0); 839 if (NULL != eh) 840 curl_easy_cleanup (eh); 841 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 842 } 843 /* Help debug #11305 */ 844 GNUNET_assert (CURLE_OK == 845 curl_easy_setopt (eh, 846 CURLOPT_VERBOSE, 847 1)); 848 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 849 "URL for batch-deposit: `%s'\n", 850 pbdh->url); 851 pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx, 852 eh, 853 pbdh->post_ctx.headers, 854 &handle_deposit_finished, 855 pbdh); 856 if (NULL == pbdh->job) 857 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 858 return TALER_EC_NONE; 859 } 860 861 862 void 863 TALER_EXCHANGE_post_batch_deposit_cancel ( 864 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh) 865 { 866 struct TEAH_AuditorInteractionEntry *aie; 867 868 while (NULL != (aie = pbdh->ai_head)) 869 { 870 GNUNET_assert (aie->dh == pbdh); 871 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 872 "Not sending deposit confirmation to auditor `%s' due to cancellation\n", 873 aie->auditor_url); 874 TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); 875 GNUNET_CONTAINER_DLL_remove (pbdh->ai_head, 876 pbdh->ai_tail, 877 aie); 878 GNUNET_free (aie); 879 } 880 if (NULL != pbdh->job) 881 { 882 GNUNET_CURL_job_cancel (pbdh->job); 883 pbdh->job = NULL; 884 } 885 TALER_EXCHANGE_keys_decref (pbdh->keys); 886 GNUNET_free (pbdh->base_url); 887 GNUNET_free (pbdh->url); 888 GNUNET_free (pbdh->cdds); 889 TALER_curl_easy_post_finished (&pbdh->post_ctx); 890 json_decref (pbdh->deposit_obj); 891 json_decref (pbdh->response); 892 GNUNET_free (pbdh); 893 } 894 895 896 /* end of exchange_api_post-batch-deposit.c */