taler-exchange-httpd_batch-deposit.c (34470B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd_batch-deposit.c 18 * @brief Handle /batch-deposit requests; parses the POST and JSON and 19 * verifies the coin signatures before handing things off 20 * to the database. 21 * @author Florian Dold 22 * @author Benedikt Mueller 23 * @author Christian Grothoff 24 */ 25 #include "taler/platform.h" 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_json_lib.h> 28 #include <jansson.h> 29 #include <microhttpd.h> 30 #include <pthread.h> 31 #include "taler/taler_extensions_policy.h" 32 #include "taler/taler_json_lib.h" 33 #include "taler/taler_mhd_lib.h" 34 #include "taler-exchange-httpd_common_kyc.h" 35 #include "taler-exchange-httpd_batch-deposit.h" 36 #include "taler-exchange-httpd_responses.h" 37 #include "taler/taler_exchangedb_lib.h" 38 #include "taler-exchange-httpd_keys.h" 39 40 41 /** 42 * Closure for #batch_deposit_transaction. 43 */ 44 struct BatchDepositContext 45 { 46 47 /** 48 * Kept in a DLL. 49 */ 50 struct BatchDepositContext *next; 51 52 /** 53 * Kept in a DLL. 54 */ 55 struct BatchDepositContext *prev; 56 57 /** 58 * The request we are working on. 59 */ 60 struct TEH_RequestContext *rc; 61 62 /** 63 * Handle for the legitimization check. 64 */ 65 struct TEH_LegitimizationCheckHandle *lch; 66 67 /** 68 * Array with the individual coin deposit fees. 69 */ 70 struct TALER_Amount *deposit_fees; 71 72 /** 73 * Information about deposited coins. 74 */ 75 struct TALER_EXCHANGEDB_CoinDepositInformation *cdis; 76 77 /** 78 * Additional details for policy extension relevant for this 79 * deposit operation, possibly NULL! 80 */ 81 json_t *policy_json; 82 83 /** 84 * Response to return, if set. 85 */ 86 struct MHD_Response *response; 87 88 /** 89 * KYC status of the reserve used for the operation. 90 */ 91 struct TALER_EXCHANGEDB_KycStatus kyc; 92 93 /** 94 * Hash over @e policy_details, might be all zero 95 */ 96 struct TALER_ExtensionPolicyHashP h_policy; 97 98 /** 99 * Hash over the merchant's payto://-URI with the wire salt. 100 */ 101 struct TALER_MerchantWireHashP h_wire; 102 103 /** 104 * When @e policy_details are persisted, this contains the id of the record 105 * in the policy_details table. 106 */ 107 uint64_t policy_details_serial_id; 108 109 /** 110 * Hash over the normalized payto://-URI of the account we are 111 * depositing into. 112 */ 113 struct TALER_NormalizedPaytoHashP nph; 114 115 /** 116 * Our timestamp (when we received the request). 117 * Possibly updated by the transaction if the 118 * request is idempotent (was repeated). 119 */ 120 struct GNUNET_TIME_Timestamp exchange_timestamp; 121 122 /** 123 * Total amount that is accumulated with this deposit, 124 * without fee. 125 */ 126 struct TALER_Amount accumulated_total_without_fee; 127 128 /** 129 * Details about the batch deposit operation. 130 */ 131 struct TALER_EXCHANGEDB_BatchDeposit bd; 132 133 /** 134 * If @e policy_json was present, the corresponding policy extension 135 * calculates these details. These will be persisted in the policy_details 136 * table. 137 */ 138 struct TALER_PolicyDetails policy_details; 139 140 /** 141 * HTTP status to return with @e response, or 0. 142 */ 143 unsigned int http_status; 144 145 /** 146 * Our current state in the state machine. 147 */ 148 enum 149 { 150 BDC_PHASE_INIT = 0, 151 BDC_PHASE_PARSE = 1, 152 BDC_PHASE_POLICY = 2, 153 BDC_PHASE_KYC = 3, 154 BDC_PHASE_TRANSACT = 4, 155 BDC_PHASE_REPLY_SUCCESS = 5, 156 BDC_PHASE_SUSPENDED, 157 BDC_PHASE_CHECK_KYC_RESULT, 158 BDC_PHASE_GENERATE_REPLY_FAILURE, 159 BDC_PHASE_RETURN_YES, 160 BDC_PHASE_RETURN_NO, 161 } phase; 162 163 /** 164 * True, if no policy was present in the request. Then 165 * @e policy_json is NULL and @e h_policy will be all zero. 166 */ 167 bool has_no_policy; 168 169 /** 170 * KYC failed because a KYC auth transfer is needed 171 * to establish the merchant_pub. 172 */ 173 bool bad_kyc_auth; 174 }; 175 176 177 /** 178 * Head of list of suspended batch deposit operations. 179 */ 180 static struct BatchDepositContext *bdc_head; 181 182 /** 183 * Tail of list of suspended batch deposit operations. 184 */ 185 static struct BatchDepositContext *bdc_tail; 186 187 188 void 189 TEH_batch_deposit_cleanup () 190 { 191 struct BatchDepositContext *bdc; 192 193 while (NULL != (bdc = bdc_head)) 194 { 195 GNUNET_assert (BDC_PHASE_SUSPENDED == bdc->phase); 196 bdc->phase = BDC_PHASE_RETURN_NO; 197 MHD_resume_connection (bdc->rc->connection); 198 GNUNET_CONTAINER_DLL_remove (bdc_head, 199 bdc_tail, 200 bdc); 201 } 202 } 203 204 205 /** 206 * Terminate the main loop by returning the final 207 * result. 208 * 209 * @param[in,out] bdc context to update phase for 210 * @param mres MHD status to return 211 */ 212 static void 213 finish_loop (struct BatchDepositContext *bdc, 214 MHD_RESULT mres) 215 { 216 bdc->phase = (MHD_YES == mres) 217 ? BDC_PHASE_RETURN_YES 218 : BDC_PHASE_RETURN_NO; 219 } 220 221 222 /** 223 * Send confirmation of batch deposit success to client. This function will 224 * create a signed message affirming the given information and return it to 225 * the client. By this, the exchange affirms that the coins had sufficient 226 * (residual) value for the specified transaction and that it will execute the 227 * requested batch deposit operation with the given wiring details. 228 * 229 * @param[in,out] bdc information about the batch deposit 230 */ 231 static void 232 bdc_phase_reply_success ( 233 struct BatchDepositContext *bdc) 234 { 235 const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 236 const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (bd->num_cdis)]; 237 enum TALER_ErrorCode ec; 238 struct TALER_ExchangePublicKeyP pub; 239 struct TALER_ExchangeSignatureP sig; 240 241 for (unsigned int i = 0; i<bdc->bd.num_cdis; i++) 242 csigs[i] = &bd->cdis[i].csig; 243 if (TALER_EC_NONE != 244 (ec = TALER_exchange_online_deposit_confirmation_sign ( 245 &TEH_keys_exchange_sign_, 246 &bd->h_contract_terms, 247 &bdc->h_wire, 248 bdc->has_no_policy ? NULL : &bdc->h_policy, 249 bdc->exchange_timestamp, 250 bd->wire_deadline, 251 bd->refund_deadline, 252 &bdc->accumulated_total_without_fee, 253 bd->num_cdis, 254 csigs, 255 &bdc->bd.merchant_pub, 256 &pub, 257 &sig))) 258 { 259 GNUNET_break (0); 260 finish_loop (bdc, 261 TALER_MHD_reply_with_ec (bdc->rc->connection, 262 ec, 263 NULL)); 264 return; 265 } 266 finish_loop (bdc, 267 TALER_MHD_REPLY_JSON_PACK ( 268 bdc->rc->connection, 269 MHD_HTTP_OK, 270 GNUNET_JSON_pack_timestamp ("exchange_timestamp", 271 bdc->exchange_timestamp), 272 GNUNET_JSON_pack_data_auto ("exchange_pub", 273 &pub), 274 GNUNET_JSON_pack_data_auto ("exchange_sig", 275 &sig))); 276 } 277 278 279 /** 280 * Execute database transaction for /batch-deposit. Runs the transaction 281 * logic; IF it returns a non-error code, the transaction logic MUST 282 * NOT queue a MHD response. IF it returns an hard error, the 283 * transaction logic MUST queue a MHD response and set @a mhd_ret. IF 284 * it returns the soft error code, the function MAY be called again to 285 * retry and MUST not queue a MHD response. 286 * 287 * @param cls a `struct BatchDepositContext` 288 * @param connection MHD request context 289 * @param[out] mhd_ret set to MHD status on error 290 * @return transaction status 291 */ 292 static enum GNUNET_DB_QueryStatus 293 batch_deposit_transaction (void *cls, 294 struct MHD_Connection *connection, 295 MHD_RESULT *mhd_ret) 296 { 297 struct BatchDepositContext *bdc = cls; 298 const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 299 enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_HARD_ERROR; 300 uint32_t bad_balance_coin_index = UINT32_MAX; 301 bool balance_ok; 302 bool in_conflict; 303 304 /* If the deposit has a policy associated to it, persist it. This will 305 * insert or update the record. */ 306 if (! bdc->has_no_policy) 307 { 308 qs = TEH_plugin->persist_policy_details ( 309 TEH_plugin->cls, 310 &bdc->policy_details, 311 &bdc->bd.policy_details_serial_id, 312 &bdc->accumulated_total_without_fee, 313 &bdc->policy_details.fulfillment_state); 314 if (qs < 0) 315 return qs; 316 317 bdc->bd.policy_blocked = 318 bdc->policy_details.fulfillment_state != TALER_PolicyFulfillmentSuccess; 319 } 320 321 /* FIXME-#9373: replace by batch insert! */ 322 for (unsigned int i = 0; i<bdc->bd.num_cdis; i++) 323 { 324 const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi 325 = &bdc->cdis[i]; 326 uint64_t known_coin_id; 327 328 qs = TEH_make_coin_known (&cdi->coin, 329 connection, 330 &known_coin_id, 331 mhd_ret); 332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 333 "make coin known (%s) returned %d\n", 334 TALER_B2S (&cdi->coin.coin_pub), 335 qs); 336 if (qs < 0) 337 return qs; 338 } 339 340 qs = TEH_plugin->do_deposit ( 341 TEH_plugin->cls, 342 bd, 343 &bdc->exchange_timestamp, 344 &balance_ok, 345 &bad_balance_coin_index, 346 &in_conflict); 347 if (qs <= 0) 348 { 349 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 350 return qs; 351 TALER_LOG_WARNING ( 352 "Failed to store /batch-deposit information in database\n"); 353 *mhd_ret = TALER_MHD_reply_with_error ( 354 connection, 355 MHD_HTTP_INTERNAL_SERVER_ERROR, 356 TALER_EC_GENERIC_DB_STORE_FAILED, 357 "batch-deposit"); 358 return qs; 359 } 360 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 361 "do_deposit returned: %d / %s[%u] / %s\n", 362 qs, 363 balance_ok ? "balance ok" : "balance insufficient", 364 (unsigned int) bad_balance_coin_index, 365 in_conflict ? "in conflict" : "no conflict"); 366 if (in_conflict) 367 { 368 struct TALER_MerchantWireHashP h_wire; 369 370 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 371 TEH_plugin->get_wire_hash_for_contract ( 372 TEH_plugin->cls, 373 &bd->merchant_pub, 374 &bd->h_contract_terms, 375 &h_wire)) 376 { 377 TALER_LOG_WARNING ( 378 "Failed to retrieve conflicting contract details from database\n"); 379 *mhd_ret = TALER_MHD_reply_with_error ( 380 connection, 381 MHD_HTTP_INTERNAL_SERVER_ERROR, 382 TALER_EC_GENERIC_DB_STORE_FAILED, 383 "batch-deposit"); 384 return qs; 385 } 386 387 *mhd_ret 388 = TEH_RESPONSE_reply_coin_conflicting_contract ( 389 connection, 390 TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT, 391 &h_wire); 392 return GNUNET_DB_STATUS_HARD_ERROR; 393 } 394 if (! balance_ok) 395 { 396 GNUNET_assert (bad_balance_coin_index < bdc->bd.num_cdis); 397 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 398 "returning history of conflicting coin (%s)\n", 399 TALER_B2S (&bdc->cdis[bad_balance_coin_index].coin.coin_pub)); 400 *mhd_ret 401 = TEH_RESPONSE_reply_coin_insufficient_funds ( 402 connection, 403 TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, 404 &bdc->cdis[bad_balance_coin_index].coin.denom_pub_hash, 405 &bdc->cdis[bad_balance_coin_index].coin.coin_pub); 406 return GNUNET_DB_STATUS_HARD_ERROR; 407 } 408 TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++; 409 return qs; 410 } 411 412 413 /** 414 * Run database transaction. 415 * 416 * @param[in,out] bdc request context 417 */ 418 static void 419 bdc_phase_transact (struct BatchDepositContext *bdc) 420 { 421 MHD_RESULT mhd_ret; 422 423 if (GNUNET_SYSERR == 424 TEH_plugin->preflight (TEH_plugin->cls)) 425 { 426 GNUNET_break (0); 427 finish_loop (bdc, 428 TALER_MHD_reply_with_error ( 429 bdc->rc->connection, 430 MHD_HTTP_INTERNAL_SERVER_ERROR, 431 TALER_EC_GENERIC_DB_START_FAILED, 432 "preflight failure")); 433 return; 434 } 435 436 if (GNUNET_OK != 437 TEH_DB_run_transaction (bdc->rc->connection, 438 "execute batch deposit", 439 TEH_MT_REQUEST_BATCH_DEPOSIT, 440 &mhd_ret, 441 &batch_deposit_transaction, 442 bdc)) 443 { 444 finish_loop (bdc, 445 mhd_ret); 446 return; 447 } 448 bdc->phase++; 449 } 450 451 452 /** 453 * Check if the @a bdc is replayed and we already have an 454 * answer. If so, replay the existing answer and return the 455 * HTTP response. 456 * 457 * @param bdc parsed request data 458 * @return true if the request is idempotent with an existing request 459 * false if we did not find the request in the DB and did not set @a mret 460 */ 461 static bool 462 check_request_idempotent ( 463 struct BatchDepositContext *bdc) 464 { 465 const struct TEH_RequestContext *rc = bdc->rc; 466 enum GNUNET_DB_QueryStatus qs; 467 bool is_idempotent; 468 469 qs = TEH_plugin->do_check_deposit_idempotent ( 470 TEH_plugin->cls, 471 &bdc->bd, 472 &bdc->exchange_timestamp, 473 &is_idempotent); 474 if (0 > qs) 475 { 476 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 477 finish_loop (bdc, 478 TALER_MHD_reply_with_error ( 479 rc->connection, 480 MHD_HTTP_INTERNAL_SERVER_ERROR, 481 TALER_EC_GENERIC_DB_FETCH_FAILED, 482 "do_check_deposit_idempotent")); 483 return true; 484 } 485 if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || 486 (! is_idempotent) ) 487 return false; 488 bdc->phase = BDC_PHASE_REPLY_SUCCESS; 489 return true; 490 } 491 492 493 /** 494 * Check the KYC result. 495 * 496 * @param bdc storage for request processing 497 */ 498 static void 499 bdc_phase_check_kyc_result (struct BatchDepositContext *bdc) 500 { 501 /* return final positive response */ 502 if ( (! bdc->kyc.ok) || 503 (bdc->bad_kyc_auth) ) 504 { 505 if (check_request_idempotent (bdc)) 506 { 507 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 508 "Request is idempotent!\n"); 509 return; 510 } 511 /* KYC required */ 512 finish_loop (bdc, 513 TEH_RESPONSE_reply_kyc_required ( 514 bdc->rc->connection, 515 &bdc->nph, 516 &bdc->kyc, 517 bdc->bad_kyc_auth)); 518 return; 519 } 520 bdc->phase = BDC_PHASE_TRANSACT; 521 } 522 523 524 /** 525 * Function called with the result of a legitimization 526 * check. 527 * 528 * @param cls closure 529 * @param lcr legitimization check result 530 */ 531 static void 532 deposit_legi_cb ( 533 void *cls, 534 const struct TEH_LegitimizationCheckResult *lcr) 535 { 536 struct BatchDepositContext *bdc = cls; 537 538 bdc->lch = NULL; 539 GNUNET_assert (BDC_PHASE_SUSPENDED == 540 bdc->phase); 541 MHD_resume_connection (bdc->rc->connection); 542 GNUNET_CONTAINER_DLL_remove (bdc_head, 543 bdc_tail, 544 bdc); 545 TALER_MHD_daemon_trigger (); 546 if (NULL != lcr->response) 547 { 548 bdc->response = lcr->response; 549 bdc->http_status = lcr->http_status; 550 bdc->phase = BDC_PHASE_GENERATE_REPLY_FAILURE; 551 return; 552 } 553 bdc->kyc = lcr->kyc; 554 bdc->bad_kyc_auth = lcr->bad_kyc_auth; 555 bdc->phase = BDC_PHASE_CHECK_KYC_RESULT; 556 } 557 558 559 /** 560 * Function called to iterate over KYC-relevant transaction amounts for a 561 * particular time range. Called within a database transaction, so must 562 * not start a new one. 563 * 564 * @param cls closure, identifies the event type and account to iterate 565 * over events for 566 * @param limit maximum time-range for which events should be fetched 567 * (timestamp in the past) 568 * @param cb function to call on each event found, events must be returned 569 * in reverse chronological order 570 * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext 571 * @return transaction status 572 */ 573 static enum GNUNET_DB_QueryStatus 574 deposit_amount_cb ( 575 void *cls, 576 struct GNUNET_TIME_Absolute limit, 577 TALER_EXCHANGEDB_KycAmountCallback cb, 578 void *cb_cls) 579 { 580 struct BatchDepositContext *bdc = cls; 581 enum GNUNET_GenericReturnValue ret; 582 enum GNUNET_DB_QueryStatus qs; 583 584 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 585 "Signaling amount %s for KYC check during deposit\n", 586 TALER_amount2s (&bdc->accumulated_total_without_fee)); 587 ret = cb (cb_cls, 588 &bdc->accumulated_total_without_fee, 589 bdc->exchange_timestamp.abs_time); 590 GNUNET_break (GNUNET_SYSERR != ret); 591 if (GNUNET_OK != ret) 592 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 593 qs = TEH_plugin->select_deposit_amounts_for_kyc_check ( 594 TEH_plugin->cls, 595 &bdc->nph, 596 limit, 597 cb, 598 cb_cls); 599 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 600 "Got %d additional transactions for this deposit and limit %llu\n", 601 qs, 602 (unsigned long long) limit.abs_value_us); 603 GNUNET_break (qs >= 0); 604 return qs; 605 } 606 607 608 /** 609 * Run KYC check. 610 * 611 * @param[in,out] bdc request context 612 */ 613 static void 614 bdc_phase_kyc (struct BatchDepositContext *bdc) 615 { 616 if (GNUNET_YES != TEH_enable_kyc) 617 { 618 bdc->phase++; 619 return; 620 } 621 TALER_full_payto_normalize_and_hash (bdc->bd.receiver_wire_account, 622 &bdc->nph); 623 bdc->lch = TEH_legitimization_check2 ( 624 &bdc->rc->async_scope_id, 625 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, 626 bdc->bd.receiver_wire_account, 627 &bdc->nph, 628 &bdc->bd.merchant_pub, 629 &deposit_amount_cb, 630 bdc, 631 &deposit_legi_cb, 632 bdc); 633 GNUNET_assert (NULL != bdc->lch); 634 GNUNET_CONTAINER_DLL_insert (bdc_head, 635 bdc_tail, 636 bdc); 637 MHD_suspend_connection (bdc->rc->connection); 638 bdc->phase = BDC_PHASE_SUSPENDED; 639 } 640 641 642 /** 643 * Handle policy. 644 * 645 * @param[in,out] bdc request context 646 */ 647 static void 648 bdc_phase_policy (struct BatchDepositContext *bdc) 649 { 650 const char *error_hint = NULL; 651 652 if (bdc->has_no_policy) 653 { 654 bdc->phase++; 655 return; 656 } 657 if (GNUNET_OK != 658 TALER_extensions_create_policy_details ( 659 TEH_currency, 660 bdc->policy_json, 661 &bdc->policy_details, 662 &error_hint)) 663 { 664 GNUNET_break_op (0); 665 finish_loop (bdc, 666 TALER_MHD_reply_with_error ( 667 bdc->rc->connection, 668 MHD_HTTP_BAD_REQUEST, 669 TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, 670 error_hint)); 671 return; 672 } 673 674 TALER_deposit_policy_hash (bdc->policy_json, 675 &bdc->h_policy); 676 bdc->phase++; 677 } 678 679 680 /** 681 * Parse per-coin deposit information from @a jcoin 682 * into @a deposit. Fill in generic information from 683 * @a ctx. 684 * 685 * @param bdc information about the overall batch 686 * @param jcoin coin data to parse 687 * @param[out] cdi where to store the result 688 * @param[out] deposit_fee where to write the deposit fee 689 * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned, 690 * #GNUNET_SYSERR on failure and no error could be returned 691 */ 692 static enum GNUNET_GenericReturnValue 693 parse_coin (const struct BatchDepositContext *bdc, 694 json_t *jcoin, 695 struct TALER_EXCHANGEDB_CoinDepositInformation *cdi, 696 struct TALER_Amount *deposit_fee) 697 { 698 const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 699 struct GNUNET_JSON_Specification spec[] = { 700 TALER_JSON_spec_amount ("contribution", 701 TEH_currency, 702 &cdi->amount_with_fee), 703 GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", 704 &cdi->coin.denom_pub_hash), 705 TALER_JSON_spec_denom_sig ("ub_sig", 706 &cdi->coin.denom_sig), 707 GNUNET_JSON_spec_fixed_auto ("coin_pub", 708 &cdi->coin.coin_pub), 709 GNUNET_JSON_spec_mark_optional ( 710 GNUNET_JSON_spec_fixed_auto ("h_age_commitment", 711 &cdi->coin.h_age_commitment), 712 &cdi->coin.no_age_commitment), 713 GNUNET_JSON_spec_fixed_auto ("coin_sig", 714 &cdi->csig), 715 GNUNET_JSON_spec_end () 716 }; 717 enum GNUNET_GenericReturnValue res; 718 719 if (GNUNET_OK != 720 (res = TALER_MHD_parse_json_data (bdc->rc->connection, 721 jcoin, 722 spec))) 723 return res; 724 /* check denomination exists and is valid */ 725 { 726 struct TEH_DenominationKey *dk; 727 MHD_RESULT mret; 728 729 dk = TEH_keys_denomination_by_hash ( 730 &cdi->coin.denom_pub_hash, 731 bdc->rc->connection, 732 &mret); 733 if (NULL == dk) 734 { 735 GNUNET_JSON_parse_free (spec); 736 return (MHD_YES == mret) 737 ? GNUNET_NO 738 : GNUNET_SYSERR; 739 } 740 if (0 > TALER_amount_cmp (&dk->meta.value, 741 &cdi->amount_with_fee)) 742 { 743 GNUNET_break_op (0); 744 GNUNET_JSON_parse_free (spec); 745 return (MHD_YES == 746 TALER_MHD_reply_with_error ( 747 bdc->rc->connection, 748 MHD_HTTP_BAD_REQUEST, 749 TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE, 750 NULL)) 751 ? GNUNET_NO 752 : GNUNET_SYSERR; 753 } 754 if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) 755 { 756 /* This denomination is past the expiration time for deposits */ 757 GNUNET_JSON_parse_free (spec); 758 return (MHD_YES == 759 TEH_RESPONSE_reply_expired_denom_pub_hash ( 760 bdc->rc->connection, 761 &cdi->coin.denom_pub_hash, 762 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, 763 "DEPOSIT")) 764 ? GNUNET_NO 765 : GNUNET_SYSERR; 766 } 767 if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) 768 { 769 /* This denomination is not yet valid */ 770 GNUNET_JSON_parse_free (spec); 771 return (MHD_YES == 772 TEH_RESPONSE_reply_expired_denom_pub_hash ( 773 bdc->rc->connection, 774 &cdi->coin.denom_pub_hash, 775 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, 776 "DEPOSIT")) 777 ? GNUNET_NO 778 : GNUNET_SYSERR; 779 } 780 if (dk->recoup_possible) 781 { 782 /* This denomination has been revoked */ 783 GNUNET_JSON_parse_free (spec); 784 return (MHD_YES == 785 TEH_RESPONSE_reply_expired_denom_pub_hash ( 786 bdc->rc->connection, 787 &cdi->coin.denom_pub_hash, 788 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, 789 "DEPOSIT")) 790 ? GNUNET_NO 791 : GNUNET_SYSERR; 792 } 793 if (dk->denom_pub.bsign_pub_key->cipher != 794 cdi->coin.denom_sig.unblinded_sig->cipher) 795 { 796 /* denomination cipher and denomination signature cipher not the same */ 797 GNUNET_JSON_parse_free (spec); 798 return (MHD_YES == 799 TALER_MHD_reply_with_error ( 800 bdc->rc->connection, 801 MHD_HTTP_BAD_REQUEST, 802 TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, 803 NULL)) 804 ? GNUNET_NO 805 : GNUNET_SYSERR; 806 } 807 808 *deposit_fee = dk->meta.fees.deposit; 809 /* check coin signature */ 810 switch (dk->denom_pub.bsign_pub_key->cipher) 811 { 812 case GNUNET_CRYPTO_BSA_RSA: 813 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++; 814 break; 815 case GNUNET_CRYPTO_BSA_CS: 816 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++; 817 break; 818 default: 819 break; 820 } 821 if (GNUNET_YES != 822 TALER_test_coin_valid (&cdi->coin, 823 &dk->denom_pub)) 824 { 825 TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n"); 826 GNUNET_JSON_parse_free (spec); 827 return (MHD_YES == 828 TALER_MHD_reply_with_error ( 829 bdc->rc->connection, 830 MHD_HTTP_FORBIDDEN, 831 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, 832 NULL)) 833 ? GNUNET_NO 834 : GNUNET_SYSERR; 835 } 836 } 837 if (0 < TALER_amount_cmp (deposit_fee, 838 &cdi->amount_with_fee)) 839 { 840 GNUNET_break_op (0); 841 GNUNET_JSON_parse_free (spec); 842 return (MHD_YES == 843 TALER_MHD_reply_with_error ( 844 bdc->rc->connection, 845 MHD_HTTP_BAD_REQUEST, 846 TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE, 847 NULL)) 848 ? GNUNET_NO 849 : GNUNET_SYSERR; 850 } 851 852 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 853 if (GNUNET_OK != 854 TALER_wallet_deposit_verify ( 855 &cdi->amount_with_fee, 856 deposit_fee, 857 &bdc->h_wire, 858 &bd->h_contract_terms, 859 &bd->wallet_data_hash, 860 cdi->coin.no_age_commitment 861 ? NULL 862 : &cdi->coin.h_age_commitment, 863 NULL != bdc->policy_json ? &bdc->h_policy : NULL, 864 &cdi->coin.denom_pub_hash, 865 bd->wallet_timestamp, 866 &bd->merchant_pub, 867 bd->refund_deadline, 868 &cdi->coin.coin_pub, 869 &cdi->csig)) 870 { 871 TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n"); 872 GNUNET_JSON_parse_free (spec); 873 return (MHD_YES == 874 TALER_MHD_reply_with_error ( 875 bdc->rc->connection, 876 MHD_HTTP_FORBIDDEN, 877 TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID, 878 TALER_B2S (&cdi->coin.coin_pub))) 879 ? GNUNET_NO 880 : GNUNET_SYSERR; 881 } 882 return GNUNET_OK; 883 } 884 885 886 /** 887 * Run processing phase that parses the request. 888 * 889 * @param[in,out] bdc request context 890 * @param root JSON object that was POSTed 891 */ 892 static void 893 bdc_phase_parse (struct BatchDepositContext *bdc, 894 const json_t *root) 895 { 896 struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 897 const json_t *coins; 898 const json_t *policy_json; 899 bool no_refund_deadline = true; 900 struct GNUNET_JSON_Specification spec[] = { 901 TALER_JSON_spec_full_payto_uri ("merchant_payto_uri", 902 &bd->receiver_wire_account), 903 GNUNET_JSON_spec_fixed_auto ("wire_salt", 904 &bd->wire_salt), 905 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 906 &bd->merchant_pub), 907 GNUNET_JSON_spec_fixed_auto ("merchant_sig", 908 &bd->merchant_sig), 909 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 910 &bd->h_contract_terms), 911 GNUNET_JSON_spec_mark_optional ( 912 GNUNET_JSON_spec_fixed_auto ("wallet_data_hash", 913 &bd->wallet_data_hash), 914 &bd->no_wallet_data_hash), 915 GNUNET_JSON_spec_array_const ("coins", 916 &coins), 917 GNUNET_JSON_spec_mark_optional ( 918 GNUNET_JSON_spec_object_const ("policy", 919 &policy_json), 920 &bdc->has_no_policy), 921 GNUNET_JSON_spec_timestamp ("timestamp", 922 &bd->wallet_timestamp), 923 GNUNET_JSON_spec_mark_optional ( 924 GNUNET_JSON_spec_timestamp ("refund_deadline", 925 &bd->refund_deadline), 926 &no_refund_deadline), 927 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 928 &bd->wire_deadline), 929 GNUNET_JSON_spec_end () 930 }; 931 932 { 933 enum GNUNET_GenericReturnValue res; 934 935 res = TALER_MHD_parse_json_data (bdc->rc->connection, 936 root, 937 spec); 938 if (GNUNET_SYSERR == res) 939 { 940 /* hard failure */ 941 GNUNET_break (0); 942 finish_loop (bdc, 943 MHD_NO); 944 return; 945 } 946 if (GNUNET_NO == res) 947 { 948 /* failure */ 949 GNUNET_break_op (0); 950 finish_loop (bdc, 951 MHD_YES); 952 return; 953 } 954 } 955 if (GNUNET_OK != 956 TALER_merchant_contract_verify ( 957 &bd->h_contract_terms, 958 &bd->merchant_pub, 959 &bd->merchant_sig)) 960 { 961 GNUNET_break_op (0); 962 GNUNET_JSON_parse_free (spec); 963 finish_loop (bdc, 964 TALER_MHD_reply_with_error ( 965 bdc->rc->connection, 966 MHD_HTTP_BAD_REQUEST, 967 TALER_EC_GENERIC_PARAMETER_MALFORMED, 968 "merchant_sig")); 969 return; 970 } 971 bdc->policy_json 972 = json_incref ((json_t *) policy_json); 973 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 974 "Batch deposit into contract %s\n", 975 GNUNET_h2s (&bd->h_contract_terms.hash)); 976 977 /* validate merchant's wire details (as far as we can) */ 978 { 979 char *emsg; 980 981 emsg = TALER_payto_validate (bd->receiver_wire_account); 982 if (NULL != emsg) 983 { 984 MHD_RESULT ret; 985 986 GNUNET_break_op (0); 987 GNUNET_JSON_parse_free (spec); 988 ret = TALER_MHD_reply_with_error (bdc->rc->connection, 989 MHD_HTTP_BAD_REQUEST, 990 TALER_EC_GENERIC_PARAMETER_MALFORMED, 991 emsg); 992 GNUNET_free (emsg); 993 finish_loop (bdc, 994 ret); 995 return; 996 } 997 } 998 if (GNUNET_TIME_timestamp_cmp (bd->refund_deadline, 999 >, 1000 bd->wire_deadline)) 1001 { 1002 GNUNET_break_op (0); 1003 GNUNET_JSON_parse_free (spec); 1004 finish_loop (bdc, 1005 TALER_MHD_reply_with_error ( 1006 bdc->rc->connection, 1007 MHD_HTTP_BAD_REQUEST, 1008 TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE, 1009 NULL)); 1010 return; 1011 } 1012 if (GNUNET_TIME_absolute_is_never (bd->wire_deadline.abs_time)) 1013 { 1014 GNUNET_break_op (0); 1015 GNUNET_JSON_parse_free (spec); 1016 finish_loop (bdc, 1017 TALER_MHD_reply_with_error ( 1018 bdc->rc->connection, 1019 MHD_HTTP_BAD_REQUEST, 1020 TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER, 1021 NULL)); 1022 return; 1023 } 1024 TALER_full_payto_hash (bd->receiver_wire_account, 1025 &bd->wire_target_h_payto); 1026 TALER_merchant_wire_signature_hash (bd->receiver_wire_account, 1027 &bd->wire_salt, 1028 &bdc->h_wire); 1029 bd->num_cdis = json_array_size (coins); 1030 if (0 == bd->num_cdis) 1031 { 1032 GNUNET_break_op (0); 1033 GNUNET_JSON_parse_free (spec); 1034 finish_loop (bdc, 1035 TALER_MHD_reply_with_error ( 1036 bdc->rc->connection, 1037 MHD_HTTP_BAD_REQUEST, 1038 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1039 "coins")); 1040 return; 1041 } 1042 if (TALER_MAX_COINS < bd->num_cdis) 1043 { 1044 GNUNET_break_op (0); 1045 GNUNET_JSON_parse_free (spec); 1046 finish_loop (bdc, 1047 TALER_MHD_reply_with_error ( 1048 bdc->rc->connection, 1049 MHD_HTTP_BAD_REQUEST, 1050 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1051 "coins")); 1052 return; 1053 } 1054 1055 bdc->cdis 1056 = GNUNET_new_array (bd->num_cdis, 1057 struct TALER_EXCHANGEDB_CoinDepositInformation); 1058 bdc->deposit_fees 1059 = GNUNET_new_array (bd->num_cdis, 1060 struct TALER_Amount); 1061 bd->cdis = bdc->cdis; 1062 for (unsigned i = 0; i<bd->num_cdis; i++) 1063 { 1064 struct TALER_Amount amount_without_fee; 1065 enum GNUNET_GenericReturnValue res; 1066 1067 res = parse_coin (bdc, 1068 json_array_get (coins, 1069 i), 1070 &bdc->cdis[i], 1071 &bdc->deposit_fees[i]); 1072 if (GNUNET_OK != res) 1073 { 1074 finish_loop (bdc, 1075 (GNUNET_NO == res) 1076 ? MHD_YES 1077 : MHD_NO); 1078 return; 1079 } 1080 GNUNET_assert (0 <= 1081 TALER_amount_subtract ( 1082 &amount_without_fee, 1083 &bdc->cdis[i].amount_with_fee, 1084 &bdc->deposit_fees[i])); 1085 1086 GNUNET_assert (0 <= 1087 TALER_amount_add ( 1088 &bdc->accumulated_total_without_fee, 1089 &bdc->accumulated_total_without_fee, 1090 &amount_without_fee)); 1091 } 1092 1093 GNUNET_JSON_parse_free (spec); 1094 bdc->phase++; 1095 } 1096 1097 1098 /** 1099 * Function called to clean up a context. 1100 * 1101 * @param rc request context with data to clean up 1102 */ 1103 static void 1104 bdc_cleaner (struct TEH_RequestContext *rc) 1105 { 1106 struct BatchDepositContext *bdc = rc->rh_ctx; 1107 1108 if (NULL != bdc->lch) 1109 { 1110 TEH_legitimization_check_cancel (bdc->lch); 1111 bdc->lch = NULL; 1112 } 1113 if (0 != bdc->cdis) 1114 { 1115 for (unsigned int i = 0; i<bdc->bd.num_cdis; i++) 1116 TALER_denom_sig_free (&bdc->cdis[i].coin.denom_sig); 1117 GNUNET_free (bdc->cdis); 1118 } 1119 GNUNET_free (bdc->deposit_fees); 1120 json_decref (bdc->policy_json); 1121 GNUNET_free (bdc); 1122 } 1123 1124 1125 MHD_RESULT 1126 TEH_handler_batch_deposit (struct TEH_RequestContext *rc, 1127 const json_t *root, 1128 const char *const args[]) 1129 { 1130 struct BatchDepositContext *bdc = rc->rh_ctx; 1131 1132 (void) args; 1133 if (NULL == bdc) 1134 { 1135 bdc = GNUNET_new (struct BatchDepositContext); 1136 bdc->rc = rc; 1137 rc->rh_ctx = bdc; 1138 rc->rh_cleaner = &bdc_cleaner; 1139 bdc->phase = BDC_PHASE_PARSE; 1140 bdc->exchange_timestamp = GNUNET_TIME_timestamp_get (); 1141 GNUNET_assert (GNUNET_OK == 1142 TALER_amount_set_zero (TEH_currency, 1143 &bdc->accumulated_total_without_fee)); 1144 } 1145 while (1) 1146 { 1147 switch (bdc->phase) 1148 { 1149 case BDC_PHASE_INIT: 1150 GNUNET_break (0); 1151 bdc->phase = BDC_PHASE_RETURN_NO; 1152 break; 1153 case BDC_PHASE_PARSE: 1154 bdc_phase_parse (bdc, 1155 root); 1156 break; 1157 case BDC_PHASE_POLICY: 1158 bdc_phase_policy (bdc); 1159 break; 1160 case BDC_PHASE_KYC: 1161 bdc_phase_kyc (bdc); 1162 break; 1163 case BDC_PHASE_TRANSACT: 1164 bdc_phase_transact (bdc); 1165 break; 1166 case BDC_PHASE_REPLY_SUCCESS: 1167 bdc_phase_reply_success (bdc); 1168 break; 1169 case BDC_PHASE_SUSPENDED: 1170 return MHD_YES; 1171 case BDC_PHASE_CHECK_KYC_RESULT: 1172 bdc_phase_check_kyc_result (bdc); 1173 break; 1174 case BDC_PHASE_GENERATE_REPLY_FAILURE: 1175 return MHD_queue_response (bdc->rc->connection, 1176 bdc->http_status, 1177 bdc->response); 1178 case BDC_PHASE_RETURN_YES: 1179 return MHD_YES; 1180 case BDC_PHASE_RETURN_NO: 1181 return MHD_NO; 1182 } 1183 } 1184 } 1185 1186 1187 /* end of taler-exchange-httpd_batch-deposit.c */