exchange_api_post-batch-deposit.c (28003B)
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_PRECONDITION_FAILED: 578 /* could happen if we were too early and the denomination 579 is not yet valid */ 580 dr->hr.ec = TALER_JSON_get_error_code (j); 581 dr->hr.hint = TALER_JSON_get_error_hint (j); 582 break; 583 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 584 dr->hr.ec = TALER_JSON_get_error_code (j); 585 dr->hr.hint = TALER_JSON_get_error_hint (j); 586 if (GNUNET_OK != 587 TALER_EXCHANGE_parse_451 (&dr->details.unavailable_for_legal_reasons, 588 j)) 589 { 590 GNUNET_break_op (0); 591 dr->hr.http_status = 0; 592 dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 593 break; 594 } 595 break; 596 case MHD_HTTP_INTERNAL_SERVER_ERROR: 597 dr->hr.ec = TALER_JSON_get_error_code (j); 598 dr->hr.hint = TALER_JSON_get_error_hint (j); 599 /* Server had an internal issue; we should retry, but this API 600 leaves this to the application */ 601 break; 602 default: 603 /* unexpected response code */ 604 dr->hr.ec = TALER_JSON_get_error_code (j); 605 dr->hr.hint = TALER_JSON_get_error_hint (j); 606 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 607 "Unexpected response code %u/%d for exchange deposit\n", 608 (unsigned int) response_code, 609 dr->hr.ec); 610 GNUNET_break_op (0); 611 break; 612 } 613 if (NULL != dh->ai_head) 614 return; 615 finish_dh (dh); 616 } 617 618 619 struct TALER_EXCHANGE_PostBatchDepositHandle * 620 TALER_EXCHANGE_post_batch_deposit_create ( 621 struct GNUNET_CURL_Context *ctx, 622 const char *url, 623 struct TALER_EXCHANGE_Keys *keys, 624 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 625 unsigned int num_cdds, 626 const struct TALER_EXCHANGE_CoinDepositDetail cdds[static num_cdds], 627 enum TALER_ErrorCode *ec) 628 { 629 struct TALER_EXCHANGE_PostBatchDepositHandle *dh; 630 json_t *deposits; 631 const struct GNUNET_HashCode *wallet_data_hashp; 632 633 if (0 == num_cdds) 634 { 635 GNUNET_break (0); 636 *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 637 return NULL; 638 } 639 if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline, 640 >, 641 dcd->wire_deadline)) 642 { 643 GNUNET_break_op (0); 644 *ec = TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE; 645 return NULL; 646 } 647 dh = GNUNET_new (struct TALER_EXCHANGE_PostBatchDepositHandle); 648 dh->auditor_chance = AUDITOR_CHANCE; 649 dh->ctx = ctx; 650 dh->base_url = GNUNET_strdup (url); 651 dh->cdds = GNUNET_memdup (cdds, 652 num_cdds * sizeof (*cdds)); 653 dh->num_cdds = num_cdds; 654 dh->dcd = *dcd; 655 TALER_merchant_wire_signature_hash (dcd->merchant_payto_uri, 656 &dcd->wire_salt, 657 &dh->h_wire); 658 deposits = json_array (); 659 GNUNET_assert (NULL != deposits); 660 GNUNET_assert (GNUNET_OK == 661 TALER_amount_set_zero (cdds[0].amount.currency, 662 &dh->total_without_fee)); 663 for (unsigned int i = 0; i<num_cdds; i++) 664 { 665 const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i]; 666 const struct TALER_EXCHANGE_DenomPublicKey *dki; 667 const struct TALER_AgeCommitmentHashP *h_age_commitmentp; 668 struct TALER_Amount amount_without_fee; 669 670 dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys, 671 &cdd->h_denom_pub); 672 if (NULL == dki) 673 { 674 *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; 675 GNUNET_break_op (0); 676 json_decref (deposits); 677 GNUNET_free (dh->base_url); 678 GNUNET_free (dh->cdds); 679 GNUNET_free (dh); 680 return NULL; 681 } 682 if (0 > 683 TALER_amount_subtract (&amount_without_fee, 684 &cdd->amount, 685 &dki->fees.deposit)) 686 { 687 *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT; 688 GNUNET_break_op (0); 689 json_decref (deposits); 690 GNUNET_free (dh->base_url); 691 GNUNET_free (dh->cdds); 692 GNUNET_free (dh); 693 return NULL; 694 } 695 GNUNET_assert (0 <= 696 TALER_amount_add (&dh->total_without_fee, 697 &dh->total_without_fee, 698 &amount_without_fee)); 699 if (GNUNET_OK != 700 TALER_EXCHANGE_verify_deposit_signature_ (dcd, 701 &dh->h_policy, 702 &dh->h_wire, 703 cdd, 704 dki)) 705 { 706 *ec = TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID; 707 GNUNET_break_op (0); 708 json_decref (deposits); 709 GNUNET_free (dh->base_url); 710 GNUNET_free (dh->cdds); 711 GNUNET_free (dh); 712 return NULL; 713 } 714 if (GNUNET_is_zero (&cdd->h_age_commitment)) 715 h_age_commitmentp = NULL; 716 else 717 h_age_commitmentp = &cdd->h_age_commitment; 718 GNUNET_assert ( 719 0 == 720 json_array_append_new ( 721 deposits, 722 GNUNET_JSON_PACK ( 723 TALER_JSON_pack_amount ("contribution", 724 &cdd->amount), 725 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 726 &cdd->h_denom_pub), 727 TALER_JSON_pack_denom_sig ("ub_sig", 728 &cdd->denom_sig), 729 GNUNET_JSON_pack_data_auto ("coin_pub", 730 &cdd->coin_pub), 731 GNUNET_JSON_pack_allow_null ( 732 GNUNET_JSON_pack_data_auto ("h_age_commitment", 733 h_age_commitmentp)), 734 GNUNET_JSON_pack_data_auto ("coin_sig", 735 &cdd->coin_sig) 736 ))); 737 } 738 739 if (GNUNET_is_zero (&dcd->wallet_data_hash)) 740 wallet_data_hashp = NULL; 741 else 742 wallet_data_hashp = &dcd->wallet_data_hash; 743 dh->deposit_obj = GNUNET_JSON_PACK ( 744 TALER_JSON_pack_full_payto ("merchant_payto_uri", 745 dcd->merchant_payto_uri), 746 GNUNET_JSON_pack_allow_null ( 747 GNUNET_JSON_pack_string ("extra_wire_subject_metadata", 748 dcd->extra_wire_subject_metadata)), 749 GNUNET_JSON_pack_data_auto ("wire_salt", 750 &dcd->wire_salt), 751 GNUNET_JSON_pack_data_auto ("h_contract_terms", 752 &dcd->h_contract_terms), 753 GNUNET_JSON_pack_array_steal ("coins", 754 deposits), 755 GNUNET_JSON_pack_allow_null ( 756 GNUNET_JSON_pack_data_auto ("wallet_data_hash", 757 wallet_data_hashp)), 758 GNUNET_JSON_pack_timestamp ("timestamp", 759 dcd->wallet_timestamp), 760 GNUNET_JSON_pack_data_auto ("merchant_pub", 761 &dcd->merchant_pub), 762 GNUNET_JSON_pack_data_auto ("merchant_sig", 763 &dcd->merchant_sig), 764 GNUNET_JSON_pack_allow_null ( 765 GNUNET_JSON_pack_timestamp ("refund_deadline", 766 dcd->refund_deadline)), 767 GNUNET_JSON_pack_timestamp ("wire_transfer_deadline", 768 dcd->wire_deadline)); 769 if (NULL == dh->deposit_obj) 770 { 771 GNUNET_break (0); 772 *ec = TALER_EC_GENERIC_ALLOCATION_FAILURE; 773 GNUNET_free (dh->base_url); 774 GNUNET_free (dh->cdds); 775 GNUNET_free (dh); 776 return NULL; 777 } 778 dh->keys = TALER_EXCHANGE_keys_incref (keys); 779 *ec = TALER_EC_NONE; 780 return dh; 781 } 782 783 784 enum GNUNET_GenericReturnValue 785 TALER_EXCHANGE_post_batch_deposit_set_options_ ( 786 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 787 unsigned int num_options, 788 const struct TALER_EXCHANGE_PostBatchDepositOptionValue options[]) 789 { 790 for (unsigned int i = 0; i < num_options; i++) 791 { 792 const struct TALER_EXCHANGE_PostBatchDepositOptionValue *opt = &options[i]; 793 switch (opt->option) 794 { 795 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_END: 796 return GNUNET_OK; 797 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_FORCE_DC: 798 pbdh->auditor_chance = 1; 799 break; 800 case TALER_EXCHANGE_POST_BATCH_DEPOSIT_OPTION_VERIFY_MERCHANT_SIG: 801 pbdh->verify_merchant_sig = true; 802 break; 803 } 804 } 805 return GNUNET_OK; 806 } 807 808 809 enum TALER_ErrorCode 810 TALER_EXCHANGE_post_batch_deposit_start ( 811 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh, 812 TALER_EXCHANGE_PostBatchDepositCallback cb, 813 TALER_EXCHANGE_POST_BATCH_DEPOSIT_RESULT_CLOSURE *cb_cls) 814 { 815 CURL *eh; 816 817 if (pbdh->verify_merchant_sig && 818 (GNUNET_OK != 819 TALER_merchant_contract_verify ( 820 &pbdh->dcd.h_contract_terms, 821 &pbdh->dcd.merchant_pub, 822 &pbdh->dcd.merchant_sig)) ) 823 { 824 GNUNET_break_op (0); 825 return TALER_EC_GENERIC_PARAMETER_MALFORMED; 826 } 827 pbdh->cb = cb; 828 pbdh->cb_cls = cb_cls; 829 pbdh->url = TALER_url_join (pbdh->base_url, 830 "batch-deposit", 831 NULL); 832 if (NULL == pbdh->url) 833 { 834 GNUNET_break (0); 835 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 836 } 837 eh = TALER_EXCHANGE_curl_easy_get_ (pbdh->url); 838 if ( (NULL == eh) || 839 (GNUNET_OK != 840 TALER_curl_easy_post (&pbdh->post_ctx, 841 eh, 842 pbdh->deposit_obj)) ) 843 { 844 GNUNET_break (0); 845 if (NULL != eh) 846 curl_easy_cleanup (eh); 847 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 848 } 849 /* FIXME: Help debug #11305 */ 850 #if DEBUG 851 GNUNET_assert (CURLE_OK == 852 curl_easy_setopt (eh, 853 CURLOPT_VERBOSE, 854 1)); 855 #endif 856 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 857 "URL for batch-deposit: `%s'\n", 858 pbdh->url); 859 pbdh->job = GNUNET_CURL_job_add2 (pbdh->ctx, 860 eh, 861 pbdh->post_ctx.headers, 862 &handle_deposit_finished, 863 pbdh); 864 if (NULL == pbdh->job) 865 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 866 return TALER_EC_NONE; 867 } 868 869 870 void 871 TALER_EXCHANGE_post_batch_deposit_cancel ( 872 struct TALER_EXCHANGE_PostBatchDepositHandle *pbdh) 873 { 874 struct TEAH_AuditorInteractionEntry *aie; 875 876 while (NULL != (aie = pbdh->ai_head)) 877 { 878 GNUNET_assert (aie->dh == pbdh); 879 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 880 "Not sending deposit confirmation to auditor `%s' due to cancellation\n", 881 aie->auditor_url); 882 TALER_AUDITOR_deposit_confirmation_cancel (aie->dch); 883 GNUNET_CONTAINER_DLL_remove (pbdh->ai_head, 884 pbdh->ai_tail, 885 aie); 886 GNUNET_free (aie); 887 } 888 if (NULL != pbdh->job) 889 { 890 GNUNET_CURL_job_cancel (pbdh->job); 891 pbdh->job = NULL; 892 } 893 TALER_EXCHANGE_keys_decref (pbdh->keys); 894 GNUNET_free (pbdh->base_url); 895 GNUNET_free (pbdh->url); 896 GNUNET_free (pbdh->cdds); 897 TALER_curl_easy_post_finished (&pbdh->post_ctx); 898 json_decref (pbdh->deposit_obj); 899 json_decref (pbdh->response); 900 GNUNET_free (pbdh); 901 } 902 903 904 /* end of exchange_api_post-batch-deposit.c */