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