exchange_api_post-batch-deposit.c (27770B)
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_free_coin_conflict_ (&dh->dr.hr, 223 &dh->dr.details.conflict.details. 224 coin_conflict); 225 TALER_EXCHANGE_post_batch_deposit_cancel (dh); 226 } 227 228 229 /** 230 * Function called with the result from our call to the 231 * auditor's /deposit-confirmation handler. 232 * 233 * @param cls closure of type `struct TEAH_AuditorInteractionEntry *` 234 * @param dcr response 235 */ 236 static void 237 acc_confirmation_cb ( 238 void *cls, 239 const struct TALER_AUDITOR_DepositConfirmationResponse *dcr) 240 { 241 struct TEAH_AuditorInteractionEntry *aie = cls; 242 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = aie->dh; 243 244 if (MHD_HTTP_OK != dcr->hr.http_status) 245 { 246 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 247 "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n", 248 aie->auditor_url, 249 dcr->hr.http_status, 250 dcr->hr.ec); 251 } 252 GNUNET_CONTAINER_DLL_remove (dh->ai_head, 253 dh->ai_tail, 254 aie); 255 GNUNET_free (aie); 256 if (NULL == dh->ai_head) 257 finish_dh (dh); 258 } 259 260 261 /** 262 * Function called for each auditor to give us a chance to possibly 263 * launch a deposit confirmation interaction. 264 * 265 * @param cls closure 266 * @param auditor_url base URL of the auditor 267 * @param auditor_pub public key of the auditor 268 */ 269 static void 270 auditor_cb (void *cls, 271 const char *auditor_url, 272 const struct TALER_AuditorPublicKeyP *auditor_pub) 273 { 274 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls; 275 const struct TALER_EXCHANGE_SigningPublicKey *spk; 276 struct TEAH_AuditorInteractionEntry *aie; 277 const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL ( 278 dh->num_cdds)]; 279 const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL ( 280 dh->num_cdds)]; 281 282 for (unsigned int i = 0; i<dh->num_cdds; i++) 283 { 284 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i]; 285 286 csigs[i] = &cdd->coin_sig; 287 cpubs[i] = &cdd->coin_pub; 288 } 289 290 if (0 != 291 GNUNET_CRYPTO_random_u32 (dh->auditor_chance)) 292 { 293 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 294 "Not providing deposit confirmation to auditor\n"); 295 return; 296 } 297 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 298 "Will provide deposit confirmation to auditor `%s'\n", 299 TALER_B2S (auditor_pub)); 300 spk = TALER_EXCHANGE_get_signing_key_info (dh->keys, 301 &dh->exchange_pub); 302 if (NULL == spk) 303 { 304 GNUNET_break_op (0); 305 return; 306 } 307 aie = GNUNET_new (struct TEAH_AuditorInteractionEntry); 308 aie->dh = dh; 309 aie->auditor_url = auditor_url; 310 aie->dch = TALER_AUDITOR_deposit_confirmation ( 311 dh->ctx, 312 auditor_url, 313 &dh->h_wire, 314 &dh->h_policy, 315 &dh->dcd.h_contract_terms, 316 dh->exchange_timestamp, 317 dh->dcd.wire_deadline, 318 dh->dcd.refund_deadline, 319 &dh->total_without_fee, 320 dh->num_cdds, 321 cpubs, 322 csigs, 323 &dh->dcd.merchant_pub, 324 &dh->exchange_pub, 325 &dh->exchange_sig, 326 &dh->keys->master_pub, 327 spk->valid_from, 328 spk->valid_until, 329 spk->valid_legal, 330 &spk->master_sig, 331 &acc_confirmation_cb, 332 aie); 333 if (NULL == aie->dch) 334 { 335 GNUNET_break (0); 336 GNUNET_free (aie); 337 return; 338 } 339 GNUNET_CONTAINER_DLL_insert (dh->ai_head, 340 dh->ai_tail, 341 aie); 342 } 343 344 345 /** 346 * Find the deposited coin @a coin_pub and how we used it. 347 * 348 * @param cls a `struct TALER_EXCHANGE_PostBatchDepositHandle *` 349 * @param coin_pub public key of the coin named in the conflict reply 350 * @param[out] h_denom_pub set to the hash of the denomination we used 351 * @param[out] h_age_commitment set to the age commitment hash we used, or NULL 352 * @return #GNUNET_OK if found, #GNUNET_NO if the coin is not ours 353 */ 354 static enum GNUNET_GenericReturnValue 355 deposit_coin_lookup (void *cls, 356 const struct TALER_CoinSpendPublicKeyP *coin_pub, 357 const struct TALER_DenominationHashP **h_denom_pub, 358 const struct TALER_AgeCommitmentHashP **h_age_commitment) 359 { 360 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls; 361 362 for (unsigned int i = 0; i<dh->num_cdds; i++) 363 { 364 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i]; 365 366 if (0 != GNUNET_memcmp (coin_pub, 367 &cdd->coin_pub)) 368 continue; 369 *h_denom_pub = &cdd->h_denom_pub; 370 *h_age_commitment = GNUNET_is_zero (&cdd->h_age_commitment) 371 ? NULL 372 : &cdd->h_age_commitment; 373 return GNUNET_OK; 374 } 375 return GNUNET_NO; 376 } 377 378 379 /** 380 * Function called when we're done processing the 381 * HTTP /batch-deposit request. 382 * 383 * @param cls the `struct TALER_EXCHANGE_PostBatchDepositHandle` 384 * @param response_code HTTP response code, 0 on error 385 * @param response parsed JSON result, NULL on error 386 */ 387 static void 388 handle_deposit_finished (void *cls, 389 long response_code, 390 const void *response) 391 { 392 struct TALER_EXCHANGE_PostBatchDepositHandle *dh = cls; 393 const json_t *j = response; 394 struct TALER_EXCHANGE_PostBatchDepositResponse *dr = &dh->dr; 395 396 dh->job = NULL; 397 dh->response = json_incref ((json_t*) j); 398 dr->hr.reply = dh->response; 399 dr->hr.http_status = (unsigned int) response_code; 400 switch (response_code) 401 { 402 case 0: 403 dr->hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 404 break; 405 case MHD_HTTP_OK: 406 { 407 bool prev33; 408 struct GNUNET_JSON_Specification spec[] = { 409 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 410 &dh->exchange_sig), 411 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 412 &dh->exchange_pub), 413 GNUNET_JSON_spec_mark_optional ( 414 TALER_JSON_spec_amount ( 415 "accumulated_total_without_fee", 416 dh->total_without_fee.currency, 417 &dr->details.ok.accumulated_total_without_fee), 418 &prev33), 419 GNUNET_JSON_spec_mark_optional ( 420 TALER_JSON_spec_web_url ("transaction_base_url", 421 &dr->details.ok.transaction_base_url), 422 NULL), 423 GNUNET_JSON_spec_timestamp ("exchange_timestamp", 424 &dh->exchange_timestamp), 425 GNUNET_JSON_spec_end () 426 }; 427 428 if (GNUNET_OK != 429 GNUNET_JSON_parse (j, 430 spec, 431 NULL, NULL)) 432 { 433 GNUNET_break_op (0); 434 dr->hr.http_status = 0; 435 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 436 break; 437 } 438 if (GNUNET_OK != 439 TALER_EXCHANGE_test_signing_key (dh->keys, 440 &dh->exchange_pub)) 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 if (prev33) 448 dr->details.ok.accumulated_total_without_fee 449 = dh->total_without_fee; 450 if (1 == 451 TALER_amount_cmp (&dh->total_without_fee, 452 &dr->details.ok.accumulated_total_without_fee)) 453 { 454 /* Amount signed by exchange is SMALLER than what we deposited */ 455 GNUNET_break_op (0); 456 dr->hr.http_status = 0; 457 dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; 458 break; 459 } 460 { 461 const struct TALER_CoinSpendSignatureP *csigs[ 462 GNUNET_NZL (dh->num_cdds)]; 463 464 for (unsigned int i = 0; i<dh->num_cdds; i++) 465 csigs[i] = &dh->cdds[i].coin_sig; 466 if (GNUNET_OK != 467 TALER_exchange_online_deposit_confirmation_verify ( 468 &dh->dcd.h_contract_terms, 469 &dh->h_wire, 470 &dh->h_policy, 471 dh->exchange_timestamp, 472 dh->dcd.wire_deadline, 473 dh->dcd.refund_deadline, 474 &dr->details.ok.accumulated_total_without_fee, 475 dh->num_cdds, 476 csigs, 477 &dh->dcd.merchant_pub, 478 &dh->exchange_pub, 479 &dh->exchange_sig)) 480 { 481 GNUNET_break_op (0); 482 dr->hr.http_status = 0; 483 dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; 484 break; 485 } 486 } 487 dh->total_without_fee = dr->details.ok.accumulated_total_without_fee; 488 TALER_EXCHANGE_get_auditors_for_dc_ (dh->keys, 489 &auditor_cb, 490 dh); 491 } 492 dr->details.ok.exchange_sig = &dh->exchange_sig; 493 dr->details.ok.exchange_pub = &dh->exchange_pub; 494 dr->details.ok.deposit_timestamp = dh->exchange_timestamp; 495 break; 496 case MHD_HTTP_BAD_REQUEST: 497 /* This should never happen, either us or the exchange is buggy 498 (or API version conflict); just pass JSON reply to the application */ 499 dr->hr.ec = TALER_JSON_get_error_code (j); 500 dr->hr.hint = TALER_JSON_get_error_hint (j); 501 break; 502 case MHD_HTTP_FORBIDDEN: 503 dr->hr.ec = TALER_JSON_get_error_code (j); 504 dr->hr.hint = TALER_JSON_get_error_hint (j); 505 /* Nothing really to verify, exchange says one of the signatures is 506 invalid; as we checked them, this should never happen, we 507 should pass the JSON reply to the application */ 508 break; 509 case MHD_HTTP_NOT_FOUND: 510 dr->hr.ec = TALER_JSON_get_error_code (j); 511 dr->hr.hint = TALER_JSON_get_error_hint (j); 512 /* Nothing really to verify, this should never 513 happen, we should pass the JSON reply to the application */ 514 break; 515 case MHD_HTTP_CONFLICT: 516 { 517 dr->hr.ec = TALER_JSON_get_error_code (j); 518 dr->hr.hint = TALER_JSON_get_error_hint (j); 519 switch (dr->hr.ec) 520 { 521 case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: 522 { 523 struct GNUNET_JSON_Specification spec[] = { 524 GNUNET_JSON_spec_fixed_auto ( 525 "coin_pub", 526 &dr->details.conflict.details.insufficient_funds.coin_pub), 527 GNUNET_JSON_spec_fixed_auto ( 528 "h_denom_pub", 529 &dr->details.conflict.details.insufficient_funds.h_denom_pub), 530 GNUNET_JSON_spec_end () 531 }; 532 533 if (GNUNET_OK != 534 GNUNET_JSON_parse (j, 535 spec, 536 NULL, NULL)) 537 { 538 GNUNET_break_op (0); 539 dr->hr.http_status = 0; 540 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 541 break; 542 } 543 } 544 break; 545 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 546 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH: 547 if (GNUNET_OK != 548 TALER_EXCHANGE_check_coin_conflict_ (dh->keys, 549 dr->hr.ec, 550 j, 551 &deposit_coin_lookup, 552 dh, 553 &dr->details.conflict.details. 554 coin_conflict)) 555 { 556 GNUNET_break_op (0); 557 dr->hr.http_status = 0; 558 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 559 } 560 break; 561 case TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT: 562 break; 563 default: 564 GNUNET_break_op (0); 565 break; 566 } 567 } 568 break; 569 case MHD_HTTP_GONE: 570 /* could happen if denomination was revoked */ 571 /* Note: one might want to check /keys for revocation 572 signature here, alas tricky in case our /keys 573 is outdated => left to clients */ 574 dr->hr.ec = TALER_JSON_get_error_code (j); 575 dr->hr.hint = TALER_JSON_get_error_hint (j); 576 break; 577 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 578 dr->hr.ec = TALER_JSON_get_error_code (j); 579 dr->hr.hint = TALER_JSON_get_error_hint (j); 580 if (GNUNET_OK != 581 TALER_EXCHANGE_parse_451 (&dr->details.unavailable_for_legal_reasons, 582 j)) 583 { 584 GNUNET_break_op (0); 585 dr->hr.http_status = 0; 586 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 587 break; 588 } 589 break; 590 case MHD_HTTP_INTERNAL_SERVER_ERROR: 591 dr->hr.ec = TALER_JSON_get_error_code (j); 592 dr->hr.hint = TALER_JSON_get_error_hint (j); 593 /* Server had an internal issue; we should retry, but this API 594 leaves this to the application */ 595 break; 596 default: 597 /* unexpected response code */ 598 dr->hr.ec = TALER_JSON_get_error_code (j); 599 dr->hr.hint = TALER_JSON_get_error_hint (j); 600 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 601 "Unexpected response code %u/%d for exchange deposit\n", 602 (unsigned int) response_code, 603 dr->hr.ec); 604 GNUNET_break_op (0); 605 break; 606 } 607 if (NULL != dh->ai_head) 608 return; 609 finish_dh (dh); 610 } 611 612 613 struct TALER_EXCHANGE_PostBatchDepositHandle * 614 TALER_EXCHANGE_post_batch_deposit_create ( 615 struct GNUNET_CURL_Context *ctx, 616 const char *url, 617 struct TALER_EXCHANGE_Keys *keys, 618 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 619 unsigned int num_cdds, 620 const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds], 621 enum TALER_ErrorCode *ec) 622 { 623 struct TALER_EXCHANGE_PostBatchDepositHandle *dh; 624 json_t *deposits; 625 const struct GNUNET_HashCode *wallet_data_hashp; 626 627 if (0 == num_cdds) 628 { 629 GNUNET_break (0); 630 *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 631 return NULL; 632 } 633 if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline, 634 >, 635 dcd->wire_deadline)) 636 { 637 GNUNET_break_op (0); 638 *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE; 639 return NULL; 640 } 641 dh = GNUNET_new (struct TALER_EXCHANGE_PostBatchDepositHandle); 642 dh->auditor_chance = AUDITOR_CHANCE; 643 dh->ctx = ctx; 644 dh->base_url = GNUNET_strdup (url); 645 dh->cdds = GNUNET_memdup (cdds, 646 num_cdds * sizeof (*cdds)); 647 dh->num_cdds = num_cdds; 648 dh->dcd = *dcd; 649 TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri, 650 &dcd->wire_salt, 651 &dh->h_wire); 652 deposits = json_array (); 653 GNUNET_assert (NULL != deposits); 654 GNUNET_assert (GNUNET_OK == 655 TALER_amount_set_zero (cdds[0].amount.currency, 656 &dh->total_without_fee)); 657 for (unsigned int i = 0; i<num_cdds; i++) 658 { 659 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i]; 660 const struct TALER_EXCHANGE_DenomPublicKey *dki; 661 const struct TALER_AgeCommitmentHashP *h_age_commitmentp; 662 struct TALER_Amount amount_without_fee; 663 664 dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 665 &cdd->h_denom_pub); 666 if (NULL == dki) 667 { 668 *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; 669 GNUNET_break_op (0); 670 json_decref (deposits); 671 GNUNET_free (dh->base_url); 672 GNUNET_free (dh->cdds); 673 GNUNET_free (dh); 674 return NULL; 675 } 676 if (0 > 677 TALER_amount_subtract (&amount_without_fee, 678 &cdd->amount, 679 &dki->fees.deposit)) 680 { 681 *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT; 682 GNUNET_break_op (0); 683 json_decref (deposits); 684 GNUNET_free (dh->base_url); 685 GNUNET_free (dh->cdds); 686 GNUNET_free (dh); 687 return NULL; 688 } 689 GNUNET_assert (0 <= 690 TALER_amount_add (&dh->total_without_fee, 691 &dh->total_without_fee, 692 &amount_without_fee)); 693 if (GNUNET_OK != 694 TALER_EXCHANGE_verify_deposit_signature_ (dcd, 695 &dh->h_policy, 696 &dh->h_wire, 697 cdd, 698 dki)) 699 { 700 *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID; 701 GNUNET_break_op (0); 702 json_decref (deposits); 703 GNUNET_free (dh->base_url); 704 GNUNET_free (dh->cdds); 705 GNUNET_free (dh); 706 return NULL; 707 } 708 if (GNUNET_is_zero (&cdd->h_age_commitment)) 709 h_age_commitmentp = NULL; 710 else 711 h_age_commitmentp = &cdd->h_age_commitment; 712 GNUNET_assert ( 713 0 == 714 json_array_append_new ( 715 deposits, 716 GNUNET_JSON_PACK ( 717 TALER_JSON_pack_amount ("contribution", 718 &cdd->amount), 719 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 720 &cdd->h_denom_pub), 721 TALER_JSON_pack_denom_sig ("ub_sig", 722 &cdd->denom_sig), 723 GNUNET_JSON_pack_data_auto ("coin_pub", 724 &cdd->coin_pub), 725 GNUNET_JSON_pack_allow_null ( 726 GNUNET_JSON_pack_data_auto ("h_age_commitment", 727 h_age_commitmentp)), 728 GNUNET_JSON_pack_data_auto ("coin_sig", 729 &cdd->coin_sig) 730 ))); 731 } 732 733 if (GNUNET_is_zero (&dcd->wallet_data_hash)) 734 wallet_data_hashp = NULL; 735 else 736 wallet_data_hashp = &dcd->wallet_data_hash; 737 dh->deposit_obj = GNUNET_JSON_PACK ( 738 TALER_JSON_pack_full_payto ("merchant_payto_uri", 739 dcd->merchant_payto_uri), 740 GNUNET_JSON_pack_allow_null ( 741 GNUNET_JSON_pack_string ("extra_wire_subject_metadata", 742 dcd->extra_wire_subject_metadata)), 743 GNUNET_JSON_pack_data_auto ("wire_salt", 744 &dcd->wire_salt), 745 GNUNET_JSON_pack_data_auto ("h_contract_terms", 746 &dcd->h_contract_terms), 747 GNUNET_JSON_pack_array_steal ("coins", 748 deposits), 749 GNUNET_JSON_pack_allow_null ( 750 GNUNET_JSON_pack_data_auto ("wallet_data_hash", 751 wallet_data_hashp)), 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 /* FIXME: Help debug #11305 */ 844 #if DEBUG 845 GNUNET_assert (CURLE_OK == 846 curl_easy_setopt (eh, 847 CURLOPT_VERBOSE, 848 1)); 849 #endif 850 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 851 "URL for batch-deposit: `%s'\n", 852 pbdh->url); 853 pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx, 854 eh, 855 pbdh->post_ctx.headers, 856 &handle_deposit_finished, 857 pbdh); 858 if (NULL == pbdh->job) 859 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 860 return TALER_EC_NONE; 861 } 862 863 864 void 865 TALER_EXCHANGE_post_batch_deposit_cancel ( 866 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh) 867 { 868 struct TEAH_AuditorInteractionEntry *aie; 869 870 while (NULL != (aie = pbdh->ai_head)) 871 { 872 GNUNET_assert (aie->dh == pbdh); 873 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 874 "Not sending deposit confirmation to auditor `%s' due to cancellation\n", 875 aie->auditor_url); 876 TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); 877 GNUNET_CONTAINER_DLL_remove (pbdh->ai_head, 878 pbdh->ai_tail, 879 aie); 880 GNUNET_free (aie); 881 } 882 if (NULL != pbdh->job) 883 { 884 GNUNET_CURL_job_cancel (pbdh->job); 885 pbdh->job = NULL; 886 } 887 TALER_EXCHANGE_keys_decref (pbdh->keys); 888 GNUNET_free (pbdh->base_url); 889 GNUNET_free (pbdh->url); 890 GNUNET_free (pbdh->cdds); 891 TALER_curl_easy_post_finished (&pbdh->post_ctx); 892 json_decref (pbdh->deposit_obj); 893 json_decref (pbdh->response); 894 GNUNET_free (pbdh); 895 } 896 897 898 /* end of exchange_api_post-batch-deposit.c */