taler-merchant-httpd_exchanges.c (34719B)
1 /* 2 This file is part of TALER 3 (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 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 <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/backend/taler-merchant-httpd_exchanges.c 18 * @brief logic this HTTPD keeps for each exchange we interact with 19 * @author Marcello Stanisci 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <taler/taler_json_lib.h> 24 #include <taler/taler_dbevents.h> 25 #include "taler-merchant-httpd_exchanges.h" 26 #include "taler-merchant-httpd.h" 27 #include "merchant-database/get_kyc_limits.h" 28 #include "merchant-database/select_exchange_keys.h" 29 #include "merchant-database/event_listen.h" 30 #include "merchant-database/event_notify.h" 31 32 33 /** 34 * How often do we retry DB transactions with soft errors? 35 */ 36 #define MAX_RETRIES 3 37 38 /** 39 * Threshold after which exponential backoff should not increase. 40 */ 41 #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \ 42 GNUNET_TIME_UNIT_SECONDS, 60) 43 44 /** 45 * This is how long /keys long-polls for, so we should 46 * allow this time between requests if there is no 47 * answer. See exchange_api_handle.c. 48 */ 49 #define LONG_POLL_THRESHOLD GNUNET_TIME_relative_multiply ( \ 50 GNUNET_TIME_UNIT_SECONDS, 120) 51 52 53 /** 54 * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation. 55 */ 56 struct TMH_EXCHANGES_KeysOperation 57 { 58 59 /** 60 * Kept in a DLL. 61 */ 62 struct TMH_EXCHANGES_KeysOperation *next; 63 64 /** 65 * Kept in a DLL. 66 */ 67 struct TMH_EXCHANGES_KeysOperation *prev; 68 69 /** 70 * Function to call with the result. 71 */ 72 TMH_EXCHANGES_Find2Continuation fc; 73 74 /** 75 * Closure for @e fc. 76 */ 77 void *fc_cls; 78 79 /** 80 * Exchange we wait for the /keys for. 81 */ 82 struct TMH_Exchange *my_exchange; 83 84 /** 85 * Task scheduled to asynchronously return the result to 86 * the find continuation. 87 */ 88 struct GNUNET_SCHEDULER_Task *at; 89 90 }; 91 92 93 /** 94 * Information about wire transfer fees of an exchange, by wire method. 95 */ 96 struct FeesByWireMethod 97 { 98 99 /** 100 * Kept in a DLL. 101 */ 102 struct FeesByWireMethod *next; 103 104 /** 105 * Kept in a DLL. 106 */ 107 struct FeesByWireMethod *prev; 108 109 /** 110 * Wire method these fees are for. 111 */ 112 char *wire_method; 113 114 /** 115 * Applicable fees, NULL if unknown/error. 116 */ 117 struct TALER_EXCHANGE_WireAggregateFees *af; 118 119 }; 120 121 122 /** 123 * Internal representation for an exchange 124 */ 125 struct TMH_Exchange 126 { 127 128 /** 129 * Kept in a DLL. 130 */ 131 struct TMH_Exchange *next; 132 133 /** 134 * Kept in a DLL. 135 */ 136 struct TMH_Exchange *prev; 137 138 /** 139 * Head of FOs pending for this exchange. 140 */ 141 struct TMH_EXCHANGES_KeysOperation *keys_head; 142 143 /** 144 * Tail of FOs pending for this exchange. 145 */ 146 struct TMH_EXCHANGES_KeysOperation *keys_tail; 147 148 /** 149 * (base) URL of the exchange. 150 */ 151 char *url; 152 153 /** 154 * Currency offered by the exchange according to OUR configuration. 155 */ 156 char *currency; 157 158 /** 159 * The keys of this exchange. 160 */ 161 struct TALER_EXCHANGE_Keys *keys; 162 163 /** 164 * Head of wire fees from /wire request. 165 */ 166 struct FeesByWireMethod *wire_fees_head; 167 168 /** 169 * Tail of wire fees from /wire request. 170 */ 171 struct FeesByWireMethod *wire_fees_tail; 172 173 /** 174 * Task to retry downloading /keys again. 175 */ 176 struct GNUNET_SCHEDULER_Task *retry_task; 177 178 /** 179 * When are we willing to force downloading again? 180 */ 181 struct GNUNET_TIME_Absolute first_retry; 182 183 /** 184 * Current exponential back-off for @e retry_task. 185 */ 186 struct GNUNET_TIME_Relative retry_delay; 187 188 /** 189 * Master public key of the exchange. 190 */ 191 struct TALER_MasterPublicKeyP master_pub; 192 193 /** 194 * true if this exchange is from our configuration and 195 * explicitly trusted, false if we need to check each 196 * key to be sure it is trusted. 197 */ 198 bool trusted; 199 200 }; 201 202 203 /** 204 * Head of exchanges we know about. 205 */ 206 static struct TMH_Exchange *exchange_head; 207 208 /** 209 * Tail of exchanges we know about. 210 */ 211 static struct TMH_Exchange *exchange_tail; 212 213 /** 214 * Our event handler listening for /keys downloads 215 * being put into the database. 216 */ 217 static struct GNUNET_DB_EventHandler *keys_eh; 218 219 /** 220 * How many exchanges do we trust (for our configured 221 * currency) as per our configuration? Used for a 222 * sanity-check on startup. 223 */ 224 static int trusted_exchange_count; 225 226 227 const struct TALER_MasterPublicKeyP * 228 TMH_EXCHANGES_get_master_pub ( 229 const struct TMH_Exchange *exchange) 230 { 231 GNUNET_break ( (exchange->trusted) || 232 (NULL != exchange->keys) ); 233 return &exchange->master_pub; 234 } 235 236 237 const char * 238 TMH_EXCHANGES_get_currency ( 239 const struct TMH_Exchange *exchange) 240 { 241 return exchange->currency; 242 } 243 244 245 struct TMH_Exchange * 246 TMH_EXCHANGES_lookup_exchange (const char *exchange_url) 247 { 248 for (struct TMH_Exchange *exchange = exchange_head; 249 NULL != exchange; 250 exchange = exchange->next) 251 if (0 == strcmp (exchange->url, 252 exchange_url)) 253 return exchange; 254 return NULL; 255 } 256 257 258 bool 259 TMH_EXCHANGES_check_trusted ( 260 const char *exchange_url) 261 { 262 struct TMH_Exchange *exchange = TMH_EXCHANGES_lookup_exchange (exchange_url); 263 264 if (NULL == exchange) 265 return false; 266 return exchange->trusted; 267 } 268 269 270 /** 271 * Check if we have any remaining pending requests for the 272 * given @a exchange, and if we have the required data, call 273 * the callback. 274 * 275 * @param exchange the exchange to check for pending find operations 276 */ 277 static void 278 process_find_operations (struct TMH_Exchange *exchange) 279 { 280 struct GNUNET_TIME_Timestamp now; 281 282 now = GNUNET_TIME_timestamp_get (); 283 for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; 284 NULL != fbw; 285 fbw = fbw->next) 286 { 287 while ( (NULL != fbw->af) && 288 (GNUNET_TIME_timestamp_cmp (fbw->af->end_date, 289 <, 290 now)) ) 291 { 292 struct TALER_EXCHANGE_WireAggregateFees *af = fbw->af; 293 294 fbw->af = af->next; 295 GNUNET_free (af); 296 } 297 if (NULL == fbw->af) 298 { 299 /* Disagreement on the current time */ 300 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 301 "Exchange has no wire fees configured for `%s' wire method\n", 302 fbw->wire_method); 303 } 304 else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date, 305 >, 306 now)) 307 { 308 /* Disagreement on the current time */ 309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 310 "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n", 311 GNUNET_TIME_relative2s ( 312 GNUNET_TIME_absolute_get_remaining ( 313 fbw->af->start_date.abs_time), 314 true)); 315 } 316 } /* for all wire methods */ 317 318 { 319 struct TMH_EXCHANGES_KeysOperation *kon; 320 321 kon = NULL; 322 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 323 "Processing find operations for `%s'\n", 324 exchange->url); 325 for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head; 326 NULL != ko; 327 ko = kon) 328 { 329 kon = ko->next; 330 ko->fc (ko->fc_cls, 331 exchange->keys, 332 exchange); 333 TMH_EXCHANGES_keys4exchange_cancel (ko); 334 } 335 } 336 } 337 338 339 /** 340 * Function called with information about the wire fees for each wire method. 341 * Stores the wire fees within our internal data structures for later use. 342 * 343 * @param exchange connection to the exchange 344 * @param master_pub public key of the exchange 345 * @param num_methods number of wire methods supported 346 * @param fbm wire fees by method 347 * @return #GNUNET_OK on success 348 */ 349 static enum GNUNET_GenericReturnValue 350 process_wire_fees ( 351 struct TMH_Exchange *exchange, 352 const struct TALER_MasterPublicKeyP *master_pub, 353 unsigned int num_methods, 354 const struct TALER_EXCHANGE_WireFeesByMethod fbm[static num_methods]) 355 { 356 for (unsigned int i = 0; i<num_methods; i++) 357 { 358 const char *wire_method = fbm[i].method; 359 const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head; 360 struct FeesByWireMethod *f; 361 struct TALER_EXCHANGE_WireAggregateFees *endp; 362 363 for (f = exchange->wire_fees_head; NULL != f; f = f->next) 364 if (0 == strcasecmp (wire_method, 365 f->wire_method)) 366 break; 367 if (NULL == f) 368 { 369 f = GNUNET_new (struct FeesByWireMethod); 370 f->wire_method = GNUNET_strdup (wire_method); 371 GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head, 372 exchange->wire_fees_tail, 373 f); 374 } 375 endp = f->af; 376 while ( (NULL != endp) && 377 (NULL != endp->next) ) 378 endp = endp->next; 379 while ( (NULL != endp) && 380 (NULL != fees) && 381 (GNUNET_TIME_timestamp_cmp (fees->start_date, 382 <, 383 endp->end_date)) ) 384 fees = fees->next; 385 if ( (NULL != endp) && 386 (NULL != fees) && 387 (GNUNET_TIME_timestamp_cmp (fees->start_date, 388 !=, 389 endp->end_date)) ) 390 { 391 /* Hole or overlap in the fee structure, not allowed! */ 392 GNUNET_break_op (0); 393 return GNUNET_SYSERR; 394 } 395 while (NULL != fees) 396 { 397 struct TALER_EXCHANGE_WireAggregateFees *af; 398 399 af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees); 400 *af = *fees; 401 af->next = NULL; 402 if (NULL == endp) 403 f->af = af; 404 else 405 endp->next = af; 406 endp = af; 407 fees = fees->next; 408 } /* all fees for this method */ 409 } /* for all methods (i) */ 410 return GNUNET_OK; 411 } 412 413 414 /** 415 * Retry getting keys from the given exchange in the closure. 416 * 417 * @param cls the `struct TMH_Exchange *` 418 */ 419 static void 420 retry_exchange (void *cls) 421 { 422 struct TMH_Exchange *exchange = cls; 423 struct GNUNET_DB_EventHeaderP es = { 424 .size = htons (sizeof (es)), 425 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS) 426 }; 427 428 exchange->retry_task = NULL; 429 exchange->retry_delay 430 = GNUNET_TIME_randomized_backoff (exchange->retry_delay, 431 RETRY_BACKOFF_THRESHOLD); 432 exchange->first_retry 433 = GNUNET_TIME_relative_to_absolute ( 434 exchange->retry_delay); 435 436 TALER_MERCHANTDB_event_notify (TMH_db, 437 &es, 438 exchange->url, 439 strlen (exchange->url) + 1); 440 } 441 442 443 /** 444 * Task to asynchronously return keys operation result to caller. 445 * 446 * @param cls a `struct TMH_EXCHANGES_KeysOperation` 447 */ 448 static void 449 return_keys (void *cls) 450 { 451 struct TMH_EXCHANGES_KeysOperation *fo = cls; 452 struct TMH_Exchange *exchange = fo->my_exchange; 453 454 fo->at = NULL; 455 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 456 "Returning key data for %s instantly\n", 457 exchange->url); 458 process_find_operations (exchange); 459 } 460 461 462 struct TMH_EXCHANGES_KeysOperation * 463 TMH_EXCHANGES_keys4exchange ( 464 const char *chosen_exchange, 465 bool force_download, 466 TMH_EXCHANGES_Find2Continuation fc, 467 void *fc_cls) 468 { 469 struct TMH_Exchange *exchange; 470 struct TMH_EXCHANGES_KeysOperation *fo; 471 472 if (NULL == TMH_curl_ctx) 473 { 474 GNUNET_break (0); 475 return NULL; 476 } 477 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 478 "Trying to find chosen exchange `%s'\n", 479 chosen_exchange); 480 exchange = TMH_EXCHANGES_lookup_exchange (chosen_exchange); 481 if (NULL == exchange) 482 { 483 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 484 "Exchange `%s' not configured\n", 485 chosen_exchange); 486 return NULL; 487 } 488 if (! exchange->trusted) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 491 "Exchange `%s' not trusted\n", 492 chosen_exchange); 493 return NULL; 494 } 495 fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation); 496 fo->fc = fc; 497 fo->fc_cls = fc_cls; 498 fo->my_exchange = exchange; 499 GNUNET_CONTAINER_DLL_insert (exchange->keys_head, 500 exchange->keys_tail, 501 fo); 502 if ( (NULL == exchange->keys) && 503 (! force_download) ) 504 { 505 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 506 "Waiting for `%skeys' already, failing query instantly\n", 507 exchange->url); 508 GNUNET_assert (NULL == fo->at); 509 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 510 fo); 511 return fo; 512 } 513 if ( (NULL != exchange->keys) && 514 (! force_download) && 515 (GNUNET_TIME_absolute_is_future ( 516 exchange->keys->key_data_expiration.abs_time)) ) 517 { 518 /* We have a valid reply, immediately return result */ 519 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 520 "The exchange `%s' is ready\n", 521 exchange->url); 522 GNUNET_assert (NULL == fo->at); 523 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 524 fo); 525 return fo; 526 } 527 if ( (force_download) && 528 (GNUNET_TIME_absolute_is_future (exchange->first_retry)) && 529 (NULL != exchange->keys) ) 530 { 531 /* Return results immediately. */ 532 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 533 "Earliest retry is in the future, returning keys now\n"); 534 fo->at = GNUNET_SCHEDULER_add_now (&return_keys, 535 fo); 536 /* *no* return here, we MAY schedule a 'retry_task' in the 537 next block if there isn't one yet */ 538 } 539 if (NULL == exchange->retry_task) 540 exchange->retry_task 541 = GNUNET_SCHEDULER_add_at (exchange->first_retry, 542 &retry_exchange, 543 exchange); 544 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 545 "Next %skeys request scheduled for %s\n", 546 exchange->url, 547 GNUNET_TIME_absolute2s ( 548 exchange->first_retry)); 549 /* No activity to launch, we are already doing so. */ 550 return fo; 551 } 552 553 554 void 555 TMH_EXCHANGES_keys4exchange_cancel ( 556 struct TMH_EXCHANGES_KeysOperation *fo) 557 { 558 struct TMH_Exchange *exchange = fo->my_exchange; 559 560 if (NULL != fo->at) 561 { 562 GNUNET_SCHEDULER_cancel (fo->at); 563 fo->at = NULL; 564 } 565 GNUNET_CONTAINER_DLL_remove (exchange->keys_head, 566 exchange->keys_tail, 567 fo); 568 GNUNET_free (fo); 569 } 570 571 572 /** 573 * Obtain applicable fees for @a exchange and @a wire_method. 574 * 575 * @param exchange the exchange to query 576 * @param now current time 577 * @param wire_method the wire method we want the fees for 578 * @return NULL if we do not have fees for this method yet 579 */ 580 static const struct FeesByWireMethod * 581 get_wire_fees (const struct TMH_Exchange *exchange, 582 struct GNUNET_TIME_Timestamp now, 583 const char *wire_method) 584 { 585 for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; 586 NULL != fbw; 587 fbw = fbw->next) 588 { 589 if (0 == strcasecmp (fbw->wire_method, 590 wire_method) ) 591 { 592 struct TALER_EXCHANGE_WireAggregateFees *af; 593 594 /* Advance through list up to current time */ 595 while ( (NULL != (af = fbw->af)) && 596 (GNUNET_TIME_timestamp_cmp (now, 597 >=, 598 af->end_date)) ) 599 { 600 fbw->af = af->next; 601 GNUNET_free (af); 602 } 603 return fbw; 604 } 605 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 606 "Exchange supports `%s' as a wire method (but we do not use that one)\n", 607 fbw->wire_method); 608 } 609 return NULL; 610 } 611 612 613 /** 614 * Free @a exchange. 615 * 616 * @param[in] exchange entry to free 617 */ 618 static void 619 free_exchange_entry (struct TMH_Exchange *exchange) 620 { 621 struct FeesByWireMethod *f; 622 623 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 624 "Releasing %s exchange %s\n", 625 exchange->trusted ? "trusted" : "untrusted", 626 exchange->url); 627 GNUNET_CONTAINER_DLL_remove (exchange_head, 628 exchange_tail, 629 exchange); 630 while (NULL != (f = exchange->wire_fees_head)) 631 { 632 struct TALER_EXCHANGE_WireAggregateFees *af; 633 634 GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head, 635 exchange->wire_fees_tail, 636 f); 637 while (NULL != (af = f->af)) 638 { 639 f->af = af->next; 640 GNUNET_free (af); 641 } 642 GNUNET_free (f->wire_method); 643 GNUNET_free (f); 644 } 645 TALER_EXCHANGE_keys_decref (exchange->keys); 646 exchange->keys = NULL; 647 if (NULL != exchange->retry_task) 648 { 649 GNUNET_SCHEDULER_cancel (exchange->retry_task); 650 exchange->retry_task = NULL; 651 } 652 GNUNET_assert (NULL == exchange->keys_head); 653 GNUNET_assert (NULL == exchange->keys_tail); 654 GNUNET_free (exchange->currency); 655 GNUNET_free (exchange->url); 656 GNUNET_free (exchange); 657 } 658 659 660 enum GNUNET_GenericReturnValue 661 TMH_EXCHANGES_lookup_wire_fee ( 662 const struct TMH_Exchange *exchange, 663 const char *wire_method, 664 struct TALER_Amount *wire_fee) 665 { 666 const struct FeesByWireMethod *fbm; 667 const struct TALER_EXCHANGE_WireAggregateFees *af; 668 669 fbm = get_wire_fees (exchange, 670 GNUNET_TIME_timestamp_get (), 671 wire_method); 672 if (NULL == fbm) 673 return GNUNET_NO; 674 af = fbm->af; 675 *wire_fee = af->fees.wire; 676 return GNUNET_OK; 677 } 678 679 680 enum TMH_ExchangeStatus 681 TMH_exchange_check_debit ( 682 const char *instance_id, 683 const struct TMH_Exchange *exchange, 684 const struct TMH_WireMethod *wm, 685 struct TALER_Amount *max_amount) 686 { 687 const struct TALER_EXCHANGE_Keys *keys = exchange->keys; 688 bool have_kyc = false; 689 bool no_access_token = true; 690 enum TMH_ExchangeStatus retry_ok = 0; 691 692 if (GNUNET_TIME_absolute_is_past (exchange->first_retry)) 693 retry_ok = TMH_ES_RETRY_OK; 694 695 if (NULL == keys) 696 return TMH_ES_NO_KEYS | retry_ok; 697 if (0 != strcasecmp (keys->currency, 698 max_amount->currency)) 699 { 700 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 701 "Currency mismatch: exchange %s uses %s, we need %s\n", 702 exchange->url, 703 keys->currency, 704 max_amount->currency); 705 return TMH_ES_NO_CURR | retry_ok; 706 } 707 { 708 struct TALER_NormalizedPayto np; 709 bool account_ok; 710 711 np = TALER_payto_normalize (wm->payto_uri); 712 account_ok = TALER_EXCHANGE_keys_test_account_allowed (keys, 713 false, 714 np); 715 GNUNET_free (np.normalized_payto); 716 if (! account_ok) 717 return TMH_ES_NO_ACC | retry_ok; 718 } 719 if (! keys->kyc_enabled) 720 return TMH_ES_OK | retry_ok; 721 722 { 723 json_t *jlimits = NULL; 724 enum GNUNET_DB_QueryStatus qs; 725 726 qs = TALER_MERCHANTDB_get_kyc_limits (TMH_db, 727 wm->payto_uri, 728 instance_id, 729 exchange->url, 730 &have_kyc, 731 &no_access_token, 732 &jlimits); 733 GNUNET_break (qs >= 0); 734 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 735 "get_kyc_limits for %s at %s returned %s/%s\n", 736 wm->payto_uri.full_payto, 737 exchange->url, 738 have_kyc ? "KYC OK" : "KYC missing", 739 NULL == jlimits ? "default limits" : "custom limits"); 740 if ( (qs > 0) && 741 (NULL != jlimits) ) 742 { 743 json_t *jlimit; 744 size_t idx; 745 struct TALER_Amount kyc_limit; 746 bool unlimited = true; 747 748 json_array_foreach (jlimits, idx, jlimit) 749 { 750 enum TALER_KYCLOGIC_KycTriggerEvent ot; 751 struct TALER_Amount threshold; 752 bool soft_limit = false; 753 struct GNUNET_JSON_Specification spec[] = { 754 TALER_JSON_spec_kycte ("operation_type", 755 &ot), 756 TALER_JSON_spec_amount_any ("threshold", 757 &threshold), 758 GNUNET_JSON_spec_mark_optional ( 759 GNUNET_JSON_spec_bool ("soft_limit", 760 &soft_limit), 761 NULL), 762 GNUNET_JSON_spec_end () 763 }; 764 765 if (GNUNET_OK != 766 GNUNET_JSON_parse (jlimit, 767 spec, 768 NULL, NULL)) 769 { 770 GNUNET_break (0); 771 continue; 772 } 773 if (soft_limit) 774 continue; 775 if ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT != ot) && 776 (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION != ot) ) 777 continue; 778 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 779 "KYC rule %u with limit %s applies\n", 780 (unsigned int) idx, 781 TALER_amount2s (&threshold)); 782 if (unlimited) 783 { 784 unlimited = false; 785 kyc_limit = threshold; 786 } 787 else 788 { 789 TALER_amount_min (&kyc_limit, 790 &kyc_limit, 791 &threshold); 792 } 793 } 794 json_decref (jlimits); 795 /* We had custom rules, do not evaluate default rules */ 796 if (! unlimited) 797 TALER_amount_min (max_amount, 798 max_amount, 799 &kyc_limit); 800 return TMH_ES_OK | retry_ok; 801 } /* END of if qs > 0, NULL != jlimits */ 802 } 803 804 /* Check zero limits *only* if we did no KYC process at all yet. 805 Because if we did, there is at least a chance that those have 806 been lifted. */ 807 if ( (no_access_token) || 808 ( (! have_kyc) && 809 (TALER_EXCHANGE_keys_evaluate_zero_limits ( 810 keys, 811 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || 812 TALER_EXCHANGE_keys_evaluate_zero_limits ( 813 keys, 814 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) ) 815 { 816 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 817 "KYC requirements of %s not satisfied\n", 818 exchange->url); 819 GNUNET_assert (GNUNET_OK == 820 TALER_amount_set_zero ( 821 max_amount->currency, 822 max_amount)); 823 return TMH_ES_OK | retry_ok; 824 } 825 /* In any case, abide by hard limits (unless we have custom rules). */ 826 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 827 "Evaluating default hard limits of %s\n", 828 exchange->url); 829 TALER_EXCHANGE_keys_evaluate_hard_limits ( 830 keys, 831 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT, 832 max_amount); 833 TALER_EXCHANGE_keys_evaluate_hard_limits ( 834 keys, 835 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION, 836 max_amount); 837 if (TALER_EXCHANGE_keys_evaluate_zero_limits ( 838 keys, 839 TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) || 840 TALER_EXCHANGE_keys_evaluate_zero_limits ( 841 keys, 842 TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) 843 { 844 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 845 "Operation is zero-limited by default\n"); 846 GNUNET_assert (GNUNET_OK == 847 TALER_amount_set_zero (max_amount->currency, 848 max_amount)); 849 } 850 return TMH_ES_OK | retry_ok; 851 } 852 853 854 void 855 TMH_exchange_get_trusted (TMH_ExchangeCallback cb, 856 void *cb_cls) 857 { 858 for (const struct TMH_Exchange *exchange = exchange_head; 859 NULL != exchange; 860 exchange = exchange->next) 861 { 862 if (! exchange->trusted) 863 { 864 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 865 "Exchange %s not trusted, skipping!\n", 866 exchange->url); 867 continue; 868 } 869 cb (cb_cls, 870 exchange->url, 871 exchange); 872 } 873 } 874 875 876 bool 877 TMH_test_exchange_configured_for_currency ( 878 const char *currency) 879 { 880 for (const struct TMH_Exchange *exchange = exchange_head; 881 NULL != exchange; 882 exchange = exchange->next) 883 { 884 if (! exchange->trusted) 885 continue; 886 if (NULL == exchange->currency) 887 continue; 888 if (0 == strcmp (currency, 889 exchange->currency)) 890 return true; 891 } 892 return false; 893 } 894 895 896 /** 897 * (Re)load of keys from DB. 898 * 899 * @param exchange exchange to reload keys of 900 */ 901 static void 902 reload_exchange_keys (struct TMH_Exchange *exchange) 903 { 904 enum GNUNET_DB_QueryStatus qs; 905 struct TALER_EXCHANGE_Keys *keys; 906 struct GNUNET_TIME_Absolute first_retry; 907 908 qs = TALER_MERCHANTDB_select_exchange_keys (TMH_db, 909 exchange->url, 910 &first_retry, 911 &keys); 912 if (qs < 0) 913 { 914 GNUNET_break (0); 915 return; 916 } 917 if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || 918 (NULL == keys) ) 919 { 920 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 921 "No keys yet for `%s'\n", 922 exchange->url); 923 return; 924 } 925 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 926 "Loading latest keys of `%s' from database\n", 927 exchange->url); 928 exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; 929 exchange->first_retry = first_retry; 930 if (NULL == exchange->currency) 931 exchange->currency = GNUNET_strdup (keys->currency); 932 if (0 != strcmp (keys->currency, 933 exchange->currency)) 934 { 935 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 936 "/keys cached in our database are for currency `%s', but we expected `%s'\n", 937 keys->currency, 938 exchange->currency); 939 return; 940 } 941 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 942 "Loaded /keys from database with %u accounts, %u fees\n", 943 keys->accounts_len, 944 keys->fees_len); 945 if (GNUNET_OK != 946 process_wire_fees (exchange, 947 &keys->master_pub, 948 keys->fees_len, 949 keys->fees)) 950 { 951 /* invalid wire fee specification given */ 952 GNUNET_break_op (0); 953 /* but: we can continue anyway, things may just not 954 work, but probably better than to not keep going. */ 955 return; 956 } 957 958 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 959 "Reloaded /keys of %s from database\n", 960 exchange->url); 961 TALER_EXCHANGE_keys_decref (exchange->keys); 962 exchange->keys = keys; 963 if ( (exchange->trusted) && 964 (0 != GNUNET_memcmp (&exchange->master_pub, 965 &keys->master_pub)) ) 966 { 967 /* master pub differs => do not trust the exchange (without auditor) */ 968 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 969 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n", 970 exchange->url); 971 exchange->trusted = false; 972 } 973 if (! exchange->trusted) 974 { 975 exchange->master_pub = keys->master_pub; 976 for (struct TMH_Exchange *e = exchange_head; 977 NULL != e; 978 e = e->next) 979 { 980 if (e == exchange) 981 continue; 982 if (! e->trusted) 983 continue; 984 if (0 == 985 GNUNET_memcmp (&e->master_pub, 986 &exchange->master_pub)) 987 exchange->trusted = true; /* same exchange, different URL => trust applies */ 988 } 989 } 990 991 process_find_operations (exchange); 992 } 993 994 995 /** 996 * Function called on each configuration section. Finds sections 997 * about exchanges, parses the entries. 998 * 999 * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *` 1000 * @param section name of the section 1001 */ 1002 static void 1003 accept_exchanges (void *cls, 1004 const char *section) 1005 { 1006 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 1007 char *url; 1008 char *mks; 1009 struct TMH_Exchange *exchange; 1010 char *currency; 1011 1012 if (GNUNET_SYSERR == trusted_exchange_count) 1013 return; 1014 if (0 != strncasecmp (section, 1015 "merchant-exchange-", 1016 strlen ("merchant-exchange-"))) 1017 return; 1018 if (GNUNET_YES == 1019 GNUNET_CONFIGURATION_get_value_yesno (cfg, 1020 section, 1021 "DISABLED")) 1022 return; 1023 if (GNUNET_OK != 1024 GNUNET_CONFIGURATION_get_value_string (cfg, 1025 section, 1026 "EXCHANGE_BASE_URL", 1027 &url)) 1028 { 1029 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1030 section, 1031 "EXCHANGE_BASE_URL"); 1032 return; 1033 } 1034 exchange = TMH_EXCHANGES_lookup_exchange (url); 1035 if (NULL != exchange) 1036 { 1037 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1038 section, 1039 "EXCHANGE_BASE_URL", 1040 "same base URL specified again"); 1041 GNUNET_free (url); 1042 return; 1043 } 1044 if (GNUNET_OK != 1045 GNUNET_CONFIGURATION_get_value_string (cfg, 1046 section, 1047 "CURRENCY", 1048 ¤cy)) 1049 { 1050 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1051 section, 1052 "CURRENCY"); 1053 GNUNET_free (url); 1054 return; 1055 } 1056 exchange = GNUNET_new (struct TMH_Exchange); 1057 exchange->url = url; 1058 exchange->currency = currency; 1059 GNUNET_CONTAINER_DLL_insert (exchange_head, 1060 exchange_tail, 1061 exchange); 1062 if (GNUNET_OK == 1063 GNUNET_CONFIGURATION_get_value_string (cfg, 1064 section, 1065 "MASTER_KEY", 1066 &mks)) 1067 { 1068 if (GNUNET_OK == 1069 GNUNET_CRYPTO_eddsa_public_key_from_string ( 1070 mks, 1071 strlen (mks), 1072 &exchange->master_pub.eddsa_pub)) 1073 { 1074 exchange->trusted = true; 1075 trusted_exchange_count++; 1076 } 1077 else 1078 { 1079 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1080 section, 1081 "MASTER_KEY", 1082 "malformed EdDSA key"); 1083 } 1084 GNUNET_free (mks); 1085 } 1086 else 1087 { 1088 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1089 "MASTER_KEY missing in section '%s', not trusting exchange\n", 1090 section); 1091 } 1092 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1093 "Setup exchange %s as %s\n", 1094 exchange->url, 1095 exchange->trusted ? "trusted" : "untrusted"); 1096 reload_exchange_keys (exchange); 1097 if (NULL != exchange->retry_task) 1098 { 1099 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1100 "Exchange at `%s' configured in multiple configuration sections (see `%s')!\n", 1101 exchange->url, 1102 section); 1103 trusted_exchange_count = GNUNET_SYSERR; 1104 return; 1105 } 1106 exchange->retry_task 1107 = GNUNET_SCHEDULER_add_now (&retry_exchange, 1108 exchange); 1109 } 1110 1111 1112 /** 1113 * Trigger (re)loading of keys from DB. 1114 * 1115 * @param cls NULL 1116 * @param extra base URL of the exchange that changed 1117 * @param extra_len number of bytes in @a extra 1118 */ 1119 static void 1120 update_exchange_keys (void *cls, 1121 const void *extra, 1122 size_t extra_len) 1123 { 1124 const char *url = extra; 1125 struct TMH_Exchange *exchange; 1126 1127 if ( (NULL == extra) || 1128 (0 == extra_len) ) 1129 { 1130 GNUNET_break (0); 1131 return; 1132 } 1133 if ('\0' != url[extra_len - 1]) 1134 { 1135 GNUNET_break (0); 1136 return; 1137 } 1138 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1139 "Received keys change notification: reload `%s'\n", 1140 url); 1141 exchange = TMH_EXCHANGES_lookup_exchange (url); 1142 GNUNET_break (NULL != exchange); 1143 if (NULL != exchange) 1144 reload_exchange_keys (exchange); 1145 } 1146 1147 1148 bool 1149 TMH_EXCHANGES_is_below_limit ( 1150 const struct TALER_EXCHANGE_Keys *keys, 1151 enum TALER_KYCLOGIC_KycTriggerEvent operation_type, 1152 const struct TALER_Amount *amount) 1153 { 1154 if (NULL == keys) 1155 { 1156 /* should only be called after we have keys! */ 1157 GNUNET_break (0); 1158 return false; 1159 } 1160 for (unsigned int i = 0; i<keys->hard_limits_length; i++) 1161 { 1162 const struct TALER_EXCHANGE_AccountLimit *al 1163 = &keys->hard_limits[i]; 1164 1165 if (operation_type != al->operation_type) 1166 continue; 1167 if (-1 == 1168 TALER_amount_cmp (&al->threshold, 1169 amount)) 1170 /* -1: threshold < amount */ 1171 return false; 1172 } 1173 return true; 1174 } 1175 1176 1177 void 1178 TMH_EXCHANGES_get_limit ( 1179 const char *exchange_url, 1180 enum TALER_KYCLOGIC_KycTriggerEvent operation_type, 1181 struct TALER_Amount *amount) 1182 { 1183 struct TMH_Exchange *exchange; 1184 const struct TALER_EXCHANGE_Keys *keys; 1185 1186 exchange = TMH_EXCHANGES_lookup_exchange (exchange_url); 1187 if ( (NULL == exchange) || 1188 (NULL == (keys = exchange->keys)) ) 1189 { 1190 GNUNET_assert (GNUNET_OK == 1191 TALER_amount_set_zero ( 1192 amount->currency, 1193 amount)); 1194 return; 1195 } 1196 for (unsigned int i = 0; i<keys->hard_limits_length; i++) 1197 { 1198 const struct TALER_EXCHANGE_AccountLimit *al 1199 = &keys->hard_limits[i]; 1200 1201 if (operation_type != al->operation_type) 1202 continue; 1203 TALER_amount_min (amount, 1204 amount, 1205 &al->threshold); 1206 } 1207 } 1208 1209 1210 enum GNUNET_GenericReturnValue 1211 TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) 1212 { 1213 /* get exchanges from the merchant configuration and try to connect to them */ 1214 { 1215 struct GNUNET_DB_EventHeaderP es = { 1216 .size = htons (sizeof (es)), 1217 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS) 1218 }; 1219 1220 GNUNET_assert (NULL == keys_eh); 1221 keys_eh = TALER_MERCHANTDB_event_listen (TMH_db, 1222 &es, 1223 GNUNET_TIME_UNIT_FOREVER_REL, 1224 &update_exchange_keys, 1225 NULL); 1226 } 1227 GNUNET_CONFIGURATION_iterate_sections (cfg, 1228 &accept_exchanges, 1229 (void *) cfg); 1230 /* build JSON with list of trusted exchanges (will be included in contracts) */ 1231 return trusted_exchange_count; 1232 } 1233 1234 1235 void 1236 TMH_EXCHANGES_done () 1237 { 1238 if (NULL != keys_eh) 1239 { 1240 TALER_MERCHANTDB_event_listen_cancel (keys_eh); 1241 keys_eh = NULL; 1242 } 1243 while (NULL != exchange_head) 1244 free_exchange_entry (exchange_head); 1245 } 1246 1247 1248 /* end of taler-merchant-httpd_get-exchanges.c */