taler-exchange-httpd_batch-deposit.c (35313B)
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 TALER_JSON_pack_amount ("accumulated_total_without_fee", 273 &bdc->accumulated_total_without_fee), 274 GNUNET_JSON_pack_data_auto ("exchange_pub", 275 &pub), 276 GNUNET_JSON_pack_data_auto ("exchange_sig", 277 &sig))); 278 } 279 280 281 /** 282 * Execute database transaction for /batch-deposit. Runs the transaction 283 * logic; IF it returns a non-error code, the transaction logic MUST 284 * NOT queue a MHD response. IF it returns an hard error, the 285 * transaction logic MUST queue a MHD response and set @a mhd_ret. IF 286 * it returns the soft error code, the function MAY be called again to 287 * retry and MUST not queue a MHD response. 288 * 289 * @param cls a `struct BatchDepositContext` 290 * @param connection MHD request context 291 * @param[out] mhd_ret set to MHD status on error 292 * @return transaction status 293 */ 294 static enum GNUNET_DB_QueryStatus 295 batch_deposit_transaction (void *cls, 296 struct MHD_Connection *connection, 297 MHD_RESULT *mhd_ret) 298 { 299 struct BatchDepositContext *bdc = cls; 300 const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 301 enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_HARD_ERROR; 302 uint32_t bad_balance_coin_index = UINT32_MAX; 303 bool balance_ok; 304 bool in_conflict; 305 306 /* If the deposit has a policy associated to it, persist it. This will 307 * insert or update the record. */ 308 if (! bdc->has_no_policy) 309 { 310 qs = TEH_plugin->persist_policy_details ( 311 TEH_plugin->cls, 312 &bdc->policy_details, 313 &bdc->bd.policy_details_serial_id, 314 &bdc->accumulated_total_without_fee, 315 &bdc->policy_details.fulfillment_state); 316 if (qs < 0) 317 return qs; 318 319 bdc->bd.policy_blocked = 320 bdc->policy_details.fulfillment_state != TALER_PolicyFulfillmentSuccess; 321 } 322 323 /* FIXME-#9373: replace by batch insert! */ 324 for (unsigned int i = 0; i<bdc->bd.num_cdis; i++) 325 { 326 const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi 327 = &bdc->cdis[i]; 328 uint64_t known_coin_id; 329 330 qs = TEH_make_coin_known (&cdi->coin, 331 connection, 332 &known_coin_id, 333 mhd_ret); 334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 335 "make coin known (%s) returned %d\n", 336 TALER_B2S (&cdi->coin.coin_pub), 337 qs); 338 if (qs < 0) 339 return qs; 340 } 341 qs = TEH_plugin->do_deposit ( 342 TEH_plugin->cls, 343 bd, 344 bdc->deposit_fees, 345 &bdc->exchange_timestamp, 346 &bdc->accumulated_total_without_fee, 347 &balance_ok, 348 &bad_balance_coin_index, 349 &in_conflict); 350 if (qs <= 0) 351 { 352 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 353 return qs; 354 TALER_LOG_WARNING ( 355 "Failed to store /batch-deposit information in database\n"); 356 *mhd_ret = TALER_MHD_reply_with_error ( 357 connection, 358 MHD_HTTP_INTERNAL_SERVER_ERROR, 359 TALER_EC_GENERIC_DB_STORE_FAILED, 360 "batch-deposit"); 361 return qs; 362 } 363 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 364 "do_deposit returned: %d / %s[%u] / %s\n", 365 qs, 366 balance_ok ? "balance ok" : "balance insufficient", 367 (unsigned int) bad_balance_coin_index, 368 in_conflict ? "in conflict" : "no conflict"); 369 if (in_conflict) 370 { 371 struct TALER_MerchantWireHashP h_wire; 372 373 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 374 TEH_plugin->get_wire_hash_for_contract ( 375 TEH_plugin->cls, 376 &bd->merchant_pub, 377 &bd->h_contract_terms, 378 &h_wire)) 379 { 380 TALER_LOG_WARNING ( 381 "Failed to retrieve conflicting contract details from database\n"); 382 *mhd_ret = TALER_MHD_reply_with_error ( 383 connection, 384 MHD_HTTP_INTERNAL_SERVER_ERROR, 385 TALER_EC_GENERIC_DB_STORE_FAILED, 386 "batch-deposit"); 387 return qs; 388 } 389 390 *mhd_ret 391 = TEH_RESPONSE_reply_coin_conflicting_contract ( 392 connection, 393 TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT, 394 &h_wire); 395 return GNUNET_DB_STATUS_HARD_ERROR; 396 } 397 if (! balance_ok) 398 { 399 GNUNET_assert (bad_balance_coin_index < bdc->bd.num_cdis); 400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 401 "returning history of conflicting coin (%s)\n", 402 TALER_B2S (&bdc->cdis[bad_balance_coin_index].coin.coin_pub)); 403 *mhd_ret 404 = TEH_RESPONSE_reply_coin_insufficient_funds ( 405 connection, 406 TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, 407 &bdc->cdis[bad_balance_coin_index].coin.denom_pub_hash, 408 &bdc->cdis[bad_balance_coin_index].coin.coin_pub); 409 return GNUNET_DB_STATUS_HARD_ERROR; 410 } 411 TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++; 412 return qs; 413 } 414 415 416 /** 417 * Run database transaction. 418 * 419 * @param[in,out] bdc request context 420 */ 421 static void 422 bdc_phase_transact (struct BatchDepositContext *bdc) 423 { 424 MHD_RESULT mhd_ret; 425 426 if (GNUNET_SYSERR == 427 TEH_plugin->preflight (TEH_plugin->cls)) 428 { 429 GNUNET_break (0); 430 finish_loop (bdc, 431 TALER_MHD_reply_with_error ( 432 bdc->rc->connection, 433 MHD_HTTP_INTERNAL_SERVER_ERROR, 434 TALER_EC_GENERIC_DB_START_FAILED, 435 "preflight failure")); 436 return; 437 } 438 439 if (GNUNET_OK != 440 TEH_DB_run_transaction (bdc->rc->connection, 441 "execute batch deposit", 442 TEH_MT_REQUEST_BATCH_DEPOSIT, 443 &mhd_ret, 444 &batch_deposit_transaction, 445 bdc)) 446 { 447 finish_loop (bdc, 448 mhd_ret); 449 return; 450 } 451 bdc->phase++; 452 } 453 454 455 /** 456 * Check if the @a bdc is replayed and we already have an 457 * answer. If so, replay the existing answer and return the 458 * HTTP response. 459 * 460 * @param bdc parsed request data 461 * @return true if the request is idempotent with an existing request 462 * false if we did not find the request in the DB and did not set @a mret 463 */ 464 static bool 465 check_request_idempotent ( 466 struct BatchDepositContext *bdc) 467 { 468 const struct TEH_RequestContext *rc = bdc->rc; 469 enum GNUNET_DB_QueryStatus qs; 470 bool is_idempotent; 471 472 qs = TEH_plugin->do_check_deposit_idempotent ( 473 TEH_plugin->cls, 474 &bdc->bd, 475 &bdc->exchange_timestamp, 476 &is_idempotent); 477 if (0 > qs) 478 { 479 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 480 finish_loop (bdc, 481 TALER_MHD_reply_with_error ( 482 rc->connection, 483 MHD_HTTP_INTERNAL_SERVER_ERROR, 484 TALER_EC_GENERIC_DB_FETCH_FAILED, 485 "do_check_deposit_idempotent")); 486 return true; 487 } 488 if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || 489 (! is_idempotent) ) 490 return false; 491 bdc->phase = BDC_PHASE_REPLY_SUCCESS; 492 return true; 493 } 494 495 496 /** 497 * Check the KYC result. 498 * 499 * @param bdc storage for request processing 500 */ 501 static void 502 bdc_phase_check_kyc_result (struct BatchDepositContext *bdc) 503 { 504 /* return final positive response */ 505 if ( (! bdc->kyc.ok) || 506 (bdc->bad_kyc_auth) ) 507 { 508 if (check_request_idempotent (bdc)) 509 { 510 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 511 "Request is idempotent!\n"); 512 return; 513 } 514 /* KYC required */ 515 finish_loop (bdc, 516 TEH_RESPONSE_reply_kyc_required ( 517 bdc->rc->connection, 518 &bdc->nph, 519 &bdc->kyc, 520 bdc->bad_kyc_auth)); 521 return; 522 } 523 bdc->phase = BDC_PHASE_TRANSACT; 524 } 525 526 527 /** 528 * Function called with the result of a legitimization 529 * check. 530 * 531 * @param cls closure 532 * @param lcr legitimization check result 533 */ 534 static void 535 deposit_legi_cb ( 536 void *cls, 537 const struct TEH_LegitimizationCheckResult *lcr) 538 { 539 struct BatchDepositContext *bdc = cls; 540 541 bdc->lch = NULL; 542 GNUNET_assert (BDC_PHASE_SUSPENDED == 543 bdc->phase); 544 MHD_resume_connection (bdc->rc->connection); 545 GNUNET_CONTAINER_DLL_remove (bdc_head, 546 bdc_tail, 547 bdc); 548 TALER_MHD_daemon_trigger (); 549 if (NULL != lcr->response) 550 { 551 bdc->response = lcr->response; 552 bdc->http_status = lcr->http_status; 553 bdc->phase = BDC_PHASE_GENERATE_REPLY_FAILURE; 554 return; 555 } 556 bdc->kyc = lcr->kyc; 557 bdc->bad_kyc_auth = lcr->bad_kyc_auth; 558 bdc->phase = BDC_PHASE_CHECK_KYC_RESULT; 559 } 560 561 562 /** 563 * Function called to iterate over KYC-relevant transaction amounts for a 564 * particular time range. Called within a database transaction, so must 565 * not start a new one. 566 * 567 * @param cls closure, identifies the event type and account to iterate 568 * over events for 569 * @param limit maximum time-range for which events should be fetched 570 * (timestamp in the past) 571 * @param cb function to call on each event found, events must be returned 572 * in reverse chronological order 573 * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext 574 * @return transaction status 575 */ 576 static enum GNUNET_DB_QueryStatus 577 deposit_amount_cb ( 578 void *cls, 579 struct GNUNET_TIME_Absolute limit, 580 TALER_EXCHANGEDB_KycAmountCallback cb, 581 void *cb_cls) 582 { 583 struct BatchDepositContext *bdc = cls; 584 enum GNUNET_GenericReturnValue ret; 585 enum GNUNET_DB_QueryStatus qs; 586 587 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 588 "Signaling amount %s for KYC check during deposit\n", 589 TALER_amount2s (&bdc->accumulated_total_without_fee)); 590 ret = cb (cb_cls, 591 &bdc->accumulated_total_without_fee, 592 bdc->exchange_timestamp.abs_time); 593 GNUNET_break (GNUNET_SYSERR != ret); 594 if (GNUNET_OK != ret) 595 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 596 qs = TEH_plugin->select_deposit_amounts_for_kyc_check ( 597 TEH_plugin->cls, 598 &bdc->nph, 599 limit, 600 cb, 601 cb_cls); 602 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 603 "Got %d additional transactions for this deposit and limit %llu\n", 604 qs, 605 (unsigned long long) limit.abs_value_us); 606 GNUNET_break (qs >= 0); 607 return qs; 608 } 609 610 611 /** 612 * Run KYC check. 613 * 614 * @param[in,out] bdc request context 615 */ 616 static void 617 bdc_phase_kyc (struct BatchDepositContext *bdc) 618 { 619 if (GNUNET_YES != TEH_enable_kyc) 620 { 621 bdc->phase++; 622 return; 623 } 624 TALER_full_payto_normalize_and_hash (bdc->bd.receiver_wire_account, 625 &bdc->nph); 626 bdc->lch = TEH_legitimization_check2 ( 627 &bdc->rc->async_scope_id, 628 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, 629 bdc->bd.receiver_wire_account, 630 &bdc->nph, 631 &bdc->bd.merchant_pub, 632 &deposit_amount_cb, 633 bdc, 634 &deposit_legi_cb, 635 bdc); 636 GNUNET_assert (NULL != bdc->lch); 637 GNUNET_CONTAINER_DLL_insert (bdc_head, 638 bdc_tail, 639 bdc); 640 MHD_suspend_connection (bdc->rc->connection); 641 bdc->phase = BDC_PHASE_SUSPENDED; 642 } 643 644 645 /** 646 * Handle policy. 647 * 648 * @param[in,out] bdc request context 649 */ 650 static void 651 bdc_phase_policy (struct BatchDepositContext *bdc) 652 { 653 const char *error_hint = NULL; 654 655 if (bdc->has_no_policy) 656 { 657 bdc->phase++; 658 return; 659 } 660 if (GNUNET_OK != 661 TALER_extensions_create_policy_details ( 662 TEH_currency, 663 bdc->policy_json, 664 &bdc->policy_details, 665 &error_hint)) 666 { 667 GNUNET_break_op (0); 668 finish_loop (bdc, 669 TALER_MHD_reply_with_error ( 670 bdc->rc->connection, 671 MHD_HTTP_BAD_REQUEST, 672 TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, 673 error_hint)); 674 return; 675 } 676 677 TALER_deposit_policy_hash (bdc->policy_json, 678 &bdc->h_policy); 679 bdc->phase++; 680 } 681 682 683 /** 684 * Parse per-coin deposit information from @a jcoin 685 * into @a deposit. Fill in generic information from 686 * @a ctx. 687 * 688 * @param bdc information about the overall batch 689 * @param jcoin coin data to parse 690 * @param[out] cdi where to store the result 691 * @param[out] deposit_fee where to write the deposit fee 692 * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned, 693 * #GNUNET_SYSERR on failure and no error could be returned 694 */ 695 static enum GNUNET_GenericReturnValue 696 parse_coin (const struct BatchDepositContext *bdc, 697 json_t *jcoin, 698 struct TALER_EXCHANGEDB_CoinDepositInformation *cdi, 699 struct TALER_Amount *deposit_fee) 700 { 701 const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 702 struct GNUNET_JSON_Specification spec[] = { 703 TALER_JSON_spec_amount ("contribution", 704 TEH_currency, 705 &cdi->amount_with_fee), 706 GNUNET_JSON_spec_fixed_auto ("denom_pub_hash", 707 &cdi->coin.denom_pub_hash), 708 TALER_JSON_spec_denom_sig ("ub_sig", 709 &cdi->coin.denom_sig), 710 GNUNET_JSON_spec_fixed_auto ("coin_pub", 711 &cdi->coin.coin_pub), 712 GNUNET_JSON_spec_mark_optional ( 713 GNUNET_JSON_spec_fixed_auto ("h_age_commitment", 714 &cdi->coin.h_age_commitment), 715 &cdi->coin.no_age_commitment), 716 GNUNET_JSON_spec_fixed_auto ("coin_sig", 717 &cdi->csig), 718 GNUNET_JSON_spec_end () 719 }; 720 enum GNUNET_GenericReturnValue res; 721 722 if (GNUNET_OK != 723 (res = TALER_MHD_parse_json_data (bdc->rc->connection, 724 jcoin, 725 spec))) 726 return res; 727 /* check denomination exists and is valid */ 728 { 729 struct TEH_DenominationKey *dk; 730 MHD_RESULT mret; 731 732 dk = TEH_keys_denomination_by_hash ( 733 &cdi->coin.denom_pub_hash, 734 bdc->rc->connection, 735 &mret); 736 if (NULL == dk) 737 { 738 GNUNET_JSON_parse_free (spec); 739 return (MHD_YES == mret) 740 ? GNUNET_NO 741 : GNUNET_SYSERR; 742 } 743 if (0 > TALER_amount_cmp (&dk->meta.value, 744 &cdi->amount_with_fee)) 745 { 746 GNUNET_break_op (0); 747 GNUNET_JSON_parse_free (spec); 748 return (MHD_YES == 749 TALER_MHD_reply_with_error ( 750 bdc->rc->connection, 751 MHD_HTTP_BAD_REQUEST, 752 TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE, 753 NULL)) 754 ? GNUNET_NO 755 : GNUNET_SYSERR; 756 } 757 if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time)) 758 { 759 /* This denomination is past the expiration time for deposits */ 760 GNUNET_JSON_parse_free (spec); 761 return (MHD_YES == 762 TEH_RESPONSE_reply_expired_denom_pub_hash ( 763 bdc->rc->connection, 764 &cdi->coin.denom_pub_hash, 765 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, 766 "DEPOSIT")) 767 ? GNUNET_NO 768 : GNUNET_SYSERR; 769 } 770 if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) 771 { 772 /* This denomination is not yet valid */ 773 GNUNET_JSON_parse_free (spec); 774 return (MHD_YES == 775 TEH_RESPONSE_reply_expired_denom_pub_hash ( 776 bdc->rc->connection, 777 &cdi->coin.denom_pub_hash, 778 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, 779 "DEPOSIT")) 780 ? GNUNET_NO 781 : GNUNET_SYSERR; 782 } 783 if (dk->recoup_possible) 784 { 785 /* This denomination has been revoked */ 786 GNUNET_JSON_parse_free (spec); 787 return (MHD_YES == 788 TEH_RESPONSE_reply_expired_denom_pub_hash ( 789 bdc->rc->connection, 790 &cdi->coin.denom_pub_hash, 791 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, 792 "DEPOSIT")) 793 ? GNUNET_NO 794 : GNUNET_SYSERR; 795 } 796 if (dk->denom_pub.bsign_pub_key->cipher != 797 cdi->coin.denom_sig.unblinded_sig->cipher) 798 { 799 /* denomination cipher and denomination signature cipher not the same */ 800 GNUNET_JSON_parse_free (spec); 801 return (MHD_YES == 802 TALER_MHD_reply_with_error ( 803 bdc->rc->connection, 804 MHD_HTTP_BAD_REQUEST, 805 TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, 806 NULL)) 807 ? GNUNET_NO 808 : GNUNET_SYSERR; 809 } 810 811 *deposit_fee = dk->meta.fees.deposit; 812 /* check coin signature */ 813 switch (dk->denom_pub.bsign_pub_key->cipher) 814 { 815 case GNUNET_CRYPTO_BSA_RSA: 816 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++; 817 break; 818 case GNUNET_CRYPTO_BSA_CS: 819 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++; 820 break; 821 default: 822 break; 823 } 824 if (GNUNET_YES != 825 TALER_test_coin_valid (&cdi->coin, 826 &dk->denom_pub)) 827 { 828 TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n"); 829 GNUNET_JSON_parse_free (spec); 830 return (MHD_YES == 831 TALER_MHD_reply_with_error ( 832 bdc->rc->connection, 833 MHD_HTTP_FORBIDDEN, 834 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID, 835 NULL)) 836 ? GNUNET_NO 837 : GNUNET_SYSERR; 838 } 839 } 840 if (0 < TALER_amount_cmp (deposit_fee, 841 &cdi->amount_with_fee)) 842 { 843 GNUNET_break_op (0); 844 GNUNET_JSON_parse_free (spec); 845 return (MHD_YES == 846 TALER_MHD_reply_with_error ( 847 bdc->rc->connection, 848 MHD_HTTP_BAD_REQUEST, 849 TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE, 850 NULL)) 851 ? GNUNET_NO 852 : GNUNET_SYSERR; 853 } 854 855 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 856 if (GNUNET_OK != 857 TALER_wallet_deposit_verify ( 858 &cdi->amount_with_fee, 859 deposit_fee, 860 &bdc->h_wire, 861 &bd->h_contract_terms, 862 &bd->wallet_data_hash, 863 cdi->coin.no_age_commitment 864 ? NULL 865 : &cdi->coin.h_age_commitment, 866 NULL != bdc->policy_json ? &bdc->h_policy : NULL, 867 &cdi->coin.denom_pub_hash, 868 bd->wallet_timestamp, 869 &bd->merchant_pub, 870 bd->refund_deadline, 871 &cdi->coin.coin_pub, 872 &cdi->csig)) 873 { 874 TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n"); 875 GNUNET_JSON_parse_free (spec); 876 return (MHD_YES == 877 TALER_MHD_reply_with_error ( 878 bdc->rc->connection, 879 MHD_HTTP_FORBIDDEN, 880 TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID, 881 TALER_B2S (&cdi->coin.coin_pub))) 882 ? GNUNET_NO 883 : GNUNET_SYSERR; 884 } 885 return GNUNET_OK; 886 } 887 888 889 /** 890 * Run processing phase that parses the request. 891 * 892 * @param[in,out] bdc request context 893 * @param root JSON object that was POSTed 894 */ 895 static void 896 bdc_phase_parse (struct BatchDepositContext *bdc, 897 const json_t *root) 898 { 899 struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd; 900 const json_t *coins; 901 const json_t *policy_json; 902 bool no_refund_deadline = true; 903 struct GNUNET_JSON_Specification spec[] = { 904 TALER_JSON_spec_full_payto_uri ("merchant_payto_uri", 905 &bd->receiver_wire_account), 906 GNUNET_JSON_spec_fixed_auto ("wire_salt", 907 &bd->wire_salt), 908 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 909 &bd->merchant_pub), 910 GNUNET_JSON_spec_fixed_auto ("merchant_sig", 911 &bd->merchant_sig), 912 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 913 &bd->h_contract_terms), 914 GNUNET_JSON_spec_mark_optional ( 915 GNUNET_JSON_spec_fixed_auto ("wallet_data_hash", 916 &bd->wallet_data_hash), 917 &bd->no_wallet_data_hash), 918 GNUNET_JSON_spec_array_const ("coins", 919 &coins), 920 GNUNET_JSON_spec_mark_optional ( 921 GNUNET_JSON_spec_object_const ("policy", 922 &policy_json), 923 &bdc->has_no_policy), 924 GNUNET_JSON_spec_timestamp ("timestamp", 925 &bd->wallet_timestamp), 926 GNUNET_JSON_spec_mark_optional ( 927 GNUNET_JSON_spec_string ("extra_wire_subject_metadata", 928 &bd->extra_wire_subject_metadata), 929 &bd->no_wallet_data_hash), 930 GNUNET_JSON_spec_mark_optional ( 931 GNUNET_JSON_spec_timestamp ("refund_deadline", 932 &bd->refund_deadline), 933 &no_refund_deadline), 934 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 935 &bd->wire_deadline), 936 GNUNET_JSON_spec_end () 937 }; 938 939 { 940 enum GNUNET_GenericReturnValue res; 941 942 res = TALER_MHD_parse_json_data (bdc->rc->connection, 943 root, 944 spec); 945 if (GNUNET_SYSERR == res) 946 { 947 /* hard failure */ 948 GNUNET_break (0); 949 finish_loop (bdc, 950 MHD_NO); 951 return; 952 } 953 if (GNUNET_NO == res) 954 { 955 /* failure */ 956 GNUNET_break_op (0); 957 finish_loop (bdc, 958 MHD_YES); 959 return; 960 } 961 } 962 if (! TALER_is_valid_subject_metadata_string ( 963 bd->extra_wire_subject_metadata)) 964 { 965 GNUNET_break_op (0); 966 GNUNET_JSON_parse_free (spec); 967 finish_loop (bdc, 968 TALER_MHD_reply_with_error ( 969 bdc->rc->connection, 970 MHD_HTTP_BAD_REQUEST, 971 TALER_EC_GENERIC_PARAMETER_MALFORMED, 972 "extra_wire_subject_metadata")); 973 return; 974 } 975 if (GNUNET_OK != 976 TALER_merchant_contract_verify ( 977 &bd->h_contract_terms, 978 &bd->merchant_pub, 979 &bd->merchant_sig)) 980 { 981 GNUNET_break_op (0); 982 GNUNET_JSON_parse_free (spec); 983 finish_loop (bdc, 984 TALER_MHD_reply_with_error ( 985 bdc->rc->connection, 986 MHD_HTTP_BAD_REQUEST, 987 TALER_EC_GENERIC_PARAMETER_MALFORMED, 988 "merchant_sig")); 989 return; 990 } 991 bdc->policy_json 992 = json_incref ((json_t *) policy_json); 993 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 994 "Batch deposit into contract %s\n", 995 GNUNET_h2s (&bd->h_contract_terms.hash)); 996 997 /* validate merchant's wire details (as far as we can) */ 998 { 999 char *emsg; 1000 1001 emsg = TALER_payto_validate (bd->receiver_wire_account); 1002 if (NULL != emsg) 1003 { 1004 MHD_RESULT ret; 1005 1006 GNUNET_break_op (0); 1007 GNUNET_JSON_parse_free (spec); 1008 ret = TALER_MHD_reply_with_error (bdc->rc->connection, 1009 MHD_HTTP_BAD_REQUEST, 1010 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1011 emsg); 1012 GNUNET_free (emsg); 1013 finish_loop (bdc, 1014 ret); 1015 return; 1016 } 1017 } 1018 if (GNUNET_TIME_timestamp_cmp (bd->refund_deadline, 1019 >, 1020 bd->wire_deadline)) 1021 { 1022 GNUNET_break_op (0); 1023 GNUNET_JSON_parse_free (spec); 1024 finish_loop (bdc, 1025 TALER_MHD_reply_with_error ( 1026 bdc->rc->connection, 1027 MHD_HTTP_BAD_REQUEST, 1028 TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE, 1029 NULL)); 1030 return; 1031 } 1032 if (GNUNET_TIME_absolute_is_never (bd->wire_deadline.abs_time)) 1033 { 1034 GNUNET_break_op (0); 1035 GNUNET_JSON_parse_free (spec); 1036 finish_loop (bdc, 1037 TALER_MHD_reply_with_error ( 1038 bdc->rc->connection, 1039 MHD_HTTP_BAD_REQUEST, 1040 TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER, 1041 NULL)); 1042 return; 1043 } 1044 TALER_full_payto_hash (bd->receiver_wire_account, 1045 &bd->wire_target_h_payto); 1046 TALER_merchant_wire_signature_hash (bd->receiver_wire_account, 1047 &bd->wire_salt, 1048 &bdc->h_wire); 1049 bd->num_cdis = json_array_size (coins); 1050 if (0 == bd->num_cdis) 1051 { 1052 GNUNET_break_op (0); 1053 GNUNET_JSON_parse_free (spec); 1054 finish_loop (bdc, 1055 TALER_MHD_reply_with_error ( 1056 bdc->rc->connection, 1057 MHD_HTTP_BAD_REQUEST, 1058 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1059 "coins")); 1060 return; 1061 } 1062 if (TALER_MAX_COINS < bd->num_cdis) 1063 { 1064 GNUNET_break_op (0); 1065 GNUNET_JSON_parse_free (spec); 1066 finish_loop (bdc, 1067 TALER_MHD_reply_with_error ( 1068 bdc->rc->connection, 1069 MHD_HTTP_BAD_REQUEST, 1070 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1071 "coins")); 1072 return; 1073 } 1074 1075 bdc->cdis 1076 = GNUNET_new_array (bd->num_cdis, 1077 struct TALER_EXCHANGEDB_CoinDepositInformation); 1078 bdc->deposit_fees 1079 = GNUNET_new_array (bd->num_cdis, 1080 struct TALER_Amount); 1081 bd->cdis = bdc->cdis; 1082 for (unsigned i = 0; i<bd->num_cdis; i++) 1083 { 1084 struct TALER_Amount amount_without_fee; 1085 enum GNUNET_GenericReturnValue res; 1086 1087 res = parse_coin (bdc, 1088 json_array_get (coins, 1089 i), 1090 &bdc->cdis[i], 1091 &bdc->deposit_fees[i]); 1092 if (GNUNET_OK != res) 1093 { 1094 finish_loop (bdc, 1095 (GNUNET_NO == res) 1096 ? MHD_YES 1097 : MHD_NO); 1098 return; 1099 } 1100 GNUNET_assert (0 <= 1101 TALER_amount_subtract ( 1102 &amount_without_fee, 1103 &bdc->cdis[i].amount_with_fee, 1104 &bdc->deposit_fees[i])); 1105 1106 GNUNET_assert (0 <= 1107 TALER_amount_add ( 1108 &bdc->accumulated_total_without_fee, 1109 &bdc->accumulated_total_without_fee, 1110 &amount_without_fee)); 1111 } 1112 1113 GNUNET_JSON_parse_free (spec); 1114 bdc->phase++; 1115 } 1116 1117 1118 /** 1119 * Function called to clean up a context. 1120 * 1121 * @param rc request context with data to clean up 1122 */ 1123 static void 1124 bdc_cleaner (struct TEH_RequestContext *rc) 1125 { 1126 struct BatchDepositContext *bdc = rc->rh_ctx; 1127 1128 if (NULL != bdc->lch) 1129 { 1130 TEH_legitimization_check_cancel (bdc->lch); 1131 bdc->lch = NULL; 1132 } 1133 if (0 != bdc->cdis) 1134 { 1135 for (unsigned int i = 0; i<bdc->bd.num_cdis; i++) 1136 TALER_denom_sig_free (&bdc->cdis[i].coin.denom_sig); 1137 GNUNET_free (bdc->cdis); 1138 } 1139 GNUNET_free (bdc->deposit_fees); 1140 json_decref (bdc->policy_json); 1141 GNUNET_free (bdc); 1142 } 1143 1144 1145 MHD_RESULT 1146 TEH_handler_batch_deposit (struct TEH_RequestContext *rc, 1147 const json_t *root, 1148 const char *const args[]) 1149 { 1150 struct BatchDepositContext *bdc = rc->rh_ctx; 1151 1152 (void) args; 1153 if (NULL == bdc) 1154 { 1155 bdc = GNUNET_new (struct BatchDepositContext); 1156 bdc->rc = rc; 1157 rc->rh_ctx = bdc; 1158 rc->rh_cleaner = &bdc_cleaner; 1159 bdc->phase = BDC_PHASE_PARSE; 1160 bdc->exchange_timestamp = GNUNET_TIME_timestamp_get (); 1161 GNUNET_assert (GNUNET_OK == 1162 TALER_amount_set_zero (TEH_currency, 1163 &bdc->accumulated_total_without_fee)); 1164 } 1165 while (1) 1166 { 1167 switch (bdc->phase) 1168 { 1169 case BDC_PHASE_INIT: 1170 GNUNET_break (0); 1171 bdc->phase = BDC_PHASE_RETURN_NO; 1172 break; 1173 case BDC_PHASE_PARSE: 1174 bdc_phase_parse (bdc, 1175 root); 1176 break; 1177 case BDC_PHASE_POLICY: 1178 bdc_phase_policy (bdc); 1179 break; 1180 case BDC_PHASE_KYC: 1181 bdc_phase_kyc (bdc); 1182 break; 1183 case BDC_PHASE_TRANSACT: 1184 bdc_phase_transact (bdc); 1185 break; 1186 case BDC_PHASE_REPLY_SUCCESS: 1187 bdc_phase_reply_success (bdc); 1188 break; 1189 case BDC_PHASE_SUSPENDED: 1190 return MHD_YES; 1191 case BDC_PHASE_CHECK_KYC_RESULT: 1192 bdc_phase_check_kyc_result (bdc); 1193 break; 1194 case BDC_PHASE_GENERATE_REPLY_FAILURE: 1195 return MHD_queue_response (bdc->rc->connection, 1196 bdc->http_status, 1197 bdc->response); 1198 case BDC_PHASE_RETURN_YES: 1199 return MHD_YES; 1200 case BDC_PHASE_RETURN_NO: 1201 return MHD_NO; 1202 } 1203 } 1204 } 1205 1206 1207 /* end of taler-exchange-httpd_batch-deposit.c */